Riy777 commited on
Commit
adcfa05
·
1 Parent(s): 07eb3a2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -174
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py (محدث بإصلاح IndentationError)
2
  import os
3
  import traceback
4
  import signal
@@ -82,7 +82,7 @@ async def initialize_services():
82
  symbol_whale_monitor_global = EnhancedWhaleMonitor(contracts_database, r2_service_global)
83
  state_manager.set_service_initialized('symbol_whale_monitor'); print(" ✅ مراقب الحيتان مهيأ")
84
  except Exception as e: print(f" ⚠️ فشل تهيئة مراقب الحيتان: {e}"); symbol_whale_monitor_global = None
85
- print(" 🔄 تهيئة DataManager..."); data_manager_global = DataManager(contracts_database, symbol_whale_monitor_global); await data_manager_global.initialize(); state_manager.set_service_initialized('data_manager'); print(" ✅ DataManager مهيأة")
86
  print(" 🔄 تهيئة LLMService..."); llm_service_global = LLMService(); llm_service_global.r2_service = r2_service_global; state_manager.set_service_initialized('llm_service'); print(" ✅ LLMService مهيأة")
87
  print(" 🔄 تهيئة محلل المشاعر..."); sentiment_analyzer_global = SentimentAnalyzer(data_manager_global); state_manager.set_service_initialized('sentiment_analyzer'); print(" ✅ محلل المشاعر مهيأ")
88
  print(" 🔄 تهيئة محرك التعلم..."); learning_engine_global = LearningEngine(r2_service_global, data_manager_global); await learning_engine_global.initialize_enhanced(); state_manager.set_service_initialized('learning_engine'); print(" ✅ محرك التعلم مهيأ")
@@ -117,49 +117,36 @@ async def process_batch_parallel(batch, ml_processor, batch_num, total_batches,
117
  """
118
  (معدلة) معالجة دفعة من الرموز بشكل متوازي وإرجاع نتائج مفصلة
119
  - تستخدم بيانات الحيتان المحملة مسبقًا
120
- - لا تحتوي على Semaphore خاص بها (يعتمد على Semaphore داخل MLProcessor)
121
  """
122
  try:
123
- # Reduced logging for clarity
124
- # print(f" 🔄 [المستهلك] بدء معالجة الدفعة {batch_num}/{total_batches} ({len(batch)} عملة)...")
125
-
126
  batch_tasks = []
127
  for symbol_data in batch:
128
- # 🔴 تمرير بيانات الحيتان المحملة مسبقًا
129
- task = asyncio.create_task(ml_processor.process_multiple_symbols_parallel([symbol_data], preloaded_whale_data)) # Pass whale data
130
  batch_tasks.append(task)
131
 
132
- # انتظار النتائج (process_multiple_symbols_parallel يعيد قائمة)
133
  batch_results_list_of_lists = await asyncio.gather(*batch_tasks, return_exceptions=True)
134
 
135
  successful_results = []
136
  low_score_results = []
137
  failed_results = []
138
 
139
- # Flatten the results and handle errors
140
  for i, result_list in enumerate(batch_results_list_of_lists):
141
  symbol = batch[i].get('symbol', 'unknown')
142
  if isinstance(result_list, Exception):
143
  failed_results.append({"symbol": symbol, "error": f"Task Execution Error: {str(result_list)}"})
144
  continue
145
 
146
- # Since we process one symbol at a time in the task, result_list should contain 0 or 1 item
147
  if result_list:
148
- result = result_list[0] # Get the actual result dictionary
149
- if isinstance(result, dict): # Check if it's a valid dict
150
  if result.get('enhanced_final_score', 0) > 0.4:
151
  successful_results.append(result)
152
  else:
153
  low_score_results.append(result)
154
  else:
155
- # Handle cases where process_multiple_symbols_parallel might return non-dict
156
  failed_results.append({"symbol": symbol, "error": f"ML processor returned invalid type: {type(result)}"})
157
  else:
158
- # Handle cases where processing returned None or empty list
159
  failed_results.append({"symbol": symbol, "error": "ML processing returned None or empty list"})
160
-
161
-
162
- # print(f" ✅ [المستهلك] اكتملت معالجة الدفعة {batch_num}: {len(successful_results)} نجاح | {len(low_score_results)} منخفض | {len(failed_results)} فشل") # Reduced logging
163
 
164
  return {'success': successful_results, 'low_score': low_score_results, 'failures': failed_results}
165
 
@@ -183,51 +170,41 @@ async def run_3_layer_analysis():
183
  all_failed_candidates = []
184
  final_layer2_candidates = []
185
  final_opportunities = []
186
- preloaded_whale_data_dict = {} # لتخزين بيانات الحيتان
187
 
188
  try:
189
  print("🎯 بدء النظام الطبقي المكون من 3 طبقات (مع فصل جلب الحيتان)...")
190
 
191
  if not await state_manager.wait_for_initialization(): print("❌ الخدمات غير مهيأة بالكامل"); return None
192
 
193
- # الطبقة 1: الفحص السريع (لا تغيير)
194
  print("\n🔍 الطبقة 1: الفحص السريع (data_manager)...")
195
  layer1_candidates = await data_manager_global.layer1_rapid_screening()
196
  if not layer1_candidates: print("❌ لم يتم العثور على مرشحين في الطبقة 1"); return None
197
  print(f"✅ تم اختيار {len(layer1_candidates)} عملة للطبقة 2")
198
  layer1_symbols = [c['symbol'] for c in layer1_candidates]
199
 
200
- # 🔴 --- الطبقة 1.5: جلب بيانات الحيتان بشكل منفصل وغير معرقل --- 🔴
201
  start_whale_fetch = time.time()
202
  print(f"\n🐋 الطبقة 1.5: بدء جلب بيانات الحيتان لـ {len(layer1_symbols)} عملة (بشكل غير معرقل)...")
203
-
204
- # مهمة جلب بيانات الحيتان
205
  async def fetch_whale_data_task(symbols, results_dict):
206
  WHALE_FETCH_CONCURRENCY = 3
207
  semaphore = asyncio.Semaphore(WHALE_FETCH_CONCURRENCY)
208
  tasks = []
209
-
210
  async def get_data_with_semaphore(symbol):
211
  async with semaphore:
212
  try:
213
  data = await data_manager_global.get_whale_data_for_symbol(symbol)
214
- if data:
215
- results_dict[symbol] = data
216
  except Exception as e:
217
  print(f" ❌ [Whale Fetch] فشل جلب بيانات الحيتان لـ {symbol}: {e}")
218
  results_dict[symbol] = {'data_available': False, 'error': str(e)}
219
-
220
- for symbol in symbols:
221
- tasks.append(asyncio.create_task(get_data_with_semaphore(symbol)))
222
-
223
  await asyncio.gather(*tasks)
224
-
225
- # تشغيل مهمة جلب الحيتان في الخلفية
226
  whale_fetcher_task = asyncio.create_task(fetch_whale_data_task(layer1_symbols, preloaded_whale_data_dict))
227
  print(" ⏳ مهمة جلب بيانات الحيتان تعمل في الخلفية...")
228
- # 🔴 --- نهاية الطبقة 1.5 --- 🔴
229
 
230
- # --- إعداد نموذج المنتج والمستهلك لبيانات OHLCV والتحليل ---
231
  DATA_QUEUE_MAX_SIZE = 2
232
  ohlcv_data_queue = asyncio.Queue(maxsize=DATA_QUEUE_MAX_SIZE)
233
  ml_results_list = []
@@ -237,59 +214,50 @@ async def run_3_layer_analysis():
237
  total_batches = (len(layer1_candidates) + batch_size - 1) // batch_size
238
  print(f" 🚀 إعداد المنتج/المستهلك (OHLCV/ML): {total_batches} دفعة متوقعة (بحجم {batch_size})")
239
 
240
- # وظيفة المستهلك (ML Consumer) - معدلة لتمرير بيانات الحيتان
241
  async def ml_consumer_task(queue: asyncio.Queue, results_list: list, whale_data_store: dict):
242
  batch_num = 0
243
  while True:
244
  try:
245
  batch_data = await queue.get()
246
  if batch_data is None: queue.task_done(); print(" 🛑 [ML Consumer] تلقى إشارة التوقف."); break
247
-
248
  batch_num += 1
249
  print(f" 📬 [ML Consumer] استلم دفعة OHLCV {batch_num}/{total_batches} ({len(batch_data)} عملة)")
250
-
251
- # 🔴 تمرير قاموس بيانات الحيتان إلى دالة المعالجة
252
  batch_results_dict = await process_batch_parallel(
253
  batch_data, ml_processor, batch_num, total_batches, whale_data_store
254
  )
255
-
256
  results_list.append(batch_results_dict)
257
  queue.task_done()
258
- print(f" ✅ [ML Consumer] أكمل معالجة الدفعة {batch_num}/{total_batches}") # Added completion log
259
-
260
- except Exception as e:
261
- print(f"❌ [ML Consumer] خطأ فادح: {e}"); traceback.print_exc(); queue.task_done()
262
 
263
- # تشغيل المستهلك (ML Consumer)
264
  print(" ▶️ [ML Consumer] بدء تشغيل مهمة المستهلك...")
265
  consumer_task = asyncio.create_task(ml_consumer_task(ohlcv_data_queue, ml_results_list, preloaded_whale_data_dict))
266
-
267
- # تشغيل المنتج (OHLCV Producer)
268
  print(" ▶️ [OHLCV Producer] بدء تشغيل مهمة المنتج (تدفق بيانات OHLCV)...")
269
- producer_task = asyncio.create_task(
270
- data_manager_global.stream_ohlcv_data(layer1_symbols, ohlcv_data_queue)
271
- )
272
 
273
- # انتظار انتهاء المنتج (OHLCV Producer)
274
- await producer_task
275
- print(" ✅ [OHLCV Producer] أنهى جلب جميع بيانات OHLCV.")
276
-
277
- # إرسال إشارة التوقف للمستهلك (ML Consumer)
278
- await ohlcv_data_queue.put(None)
279
-
280
- # انتظار انتهاء المستهلك (ML Consumer)
281
  await ohlcv_data_queue.join()
282
- await consumer_task
283
- print(" ✅ [ML Consumer] أنهى معالجة جميع الدفعات.")
284
 
285
- # 🔴 انتظار انتهاء مهمة جلب بيانات الحيتان (التي كانت تعمل في الخلفية)
286
- print(" ⏳ انتظار اكتمال مهمة جلب بيانات الحيتان...")
287
- await whale_fetcher_task
288
- end_whale_fetch = time.time()
289
- print(f" ✅ اكتمل جلب بيانات الحيتان في {end_whale_fetch - start_whale_fetch:.2f} ثانية. تم جلب بيانات لـ {len(preloaded_whale_data_dict)} عملة.")
290
- # --- نهاية نموذج المنتج والمستهلك ---
291
-
292
- # 9. تجميع النتائج (كما كان، لكن بيانات الحيتان موجودة الآن)
 
 
 
 
 
 
 
293
  print("🔄 تجميع جميع النتائج...")
294
  for batch_result in ml_results_list:
295
  for success_item in batch_result['success']:
@@ -298,19 +266,16 @@ async def run_3_layer_analysis():
298
  if l1_data:
299
  success_item['reasons_for_candidacy'] = l1_data.get('reasons', [])
300
  success_item['layer1_score'] = l1_data.get('layer1_score', 0)
301
- if symbol in preloaded_whale_data_dict:
302
- success_item['whale_data'] = preloaded_whale_data_dict[symbol]
303
- elif 'whale_data' not in success_item:
304
- success_item['whale_data'] = {'data_available': False, 'reason': 'Failed during preload'}
305
  layer2_candidates.append(success_item)
306
  all_low_score_candidates.extend(batch_result['low_score'])
307
  all_failed_candidates.extend(batch_result['failures'])
308
 
309
  print(f"✅ اكتمل التحليل المتقدم: {len(layer2_candidates)} نجاح (عالي) | {len(all_low_score_candidates)} ن��اح (منخفض) | {len(all_failed_candidates)} فشل")
310
-
311
  if not layer2_candidates: print("❌ لم يتم العثور على مرشحين في الطبقة 2")
312
 
313
- # 10. الترتيب والفلترة (كما كان)
314
  layer2_candidates.sort(key=lambda x: x.get('enhanced_final_score', 0), reverse=True)
315
  target_count = min(10, len(layer2_candidates))
316
  final_layer2_candidates = layer2_candidates[:target_count]
@@ -318,90 +283,50 @@ async def run_3_layer_analysis():
318
  await r2_service_global.save_candidates_async(final_layer2_candidates)
319
  print("\n🏆 أفضل 10 عملات من الطبقة 2:")
320
  for i, candidate in enumerate(final_layer2_candidates):
321
- score = candidate.get('enhanced_final_score', 0)
322
- strategy = candidate.get('target_strategy', 'GENERIC')
323
- mc_score = candidate.get('monte_carlo_probability')
324
- pattern = candidate.get('pattern_analysis', {}).get('pattern_detected', 'no_pattern')
325
- timeframes = candidate.get('successful_timeframes', 0)
326
- symbol = candidate.get('symbol', 'UNKNOWN')
327
-
328
  print(f" {i+1}. {symbol}: 📊 {score:.3f} | الأطر: {timeframes}/6")
329
  if mc_score is not None: print(f" 🎯 مونت كارلو: {mc_score:.3f}")
330
  print(f" 🎯 استراتيجية: {strategy} | نمط: {pattern}")
331
  whale_data = candidate.get('whale_data')
332
- if whale_data and whale_data.get('data_available'):
333
- signal = whale_data.get('trading_signal', {})
334
- print(f" 🐋 حيتان: {signal.get('action', 'HOLD')} (ثقة: {signal.get('confidence', 0):.2f}){' ⚠️' if signal.get('critical_alert') else ''}")
335
- elif whale_data and whale_data.get('error'):
336
- print(f" 🐋 حيتان: خطأ ({whale_data.get('error')[:50]}...)")
337
 
338
- # 11. الطبقة 3: التحليل بالنموذج الضخم (كما كان)
339
  print("\n🧠 الطبقة 3: التحليل بالنموذج الضخم (LLMService)...")
340
  for candidate in final_layer2_candidates:
341
  try:
342
- symbol = candidate['symbol']
343
- print(f" 🤔 تحليل {symbol} بالنموذج الضخم...")
344
- ohlcv_data = candidate.get('ohlcv')
345
  if not ohlcv_data: print(f" ⚠️ لا توجد بيانات شموع لـ {symbol}"); continue
346
  candidate['raw_ohlcv'] = ohlcv_data
347
- timeframes_count = candidate.get('successful_timeframes', 0)
348
- total_candles = sum(len(data) for data in ohlcv_data.values()) if ohlcv_data else 0
349
  if total_candles < 30: print(f" ⚠️ بيانات شموع غير كافية لـ {symbol}: {total_candles} شمعة فقط"); continue
350
  print(f" 📊 إرسال {symbol} للنموذج: {total_candles} شمعة في {timeframes_count} إطار زمني")
351
- llm_analysis = await llm_service_global.get_trading_decision(candidate) # candidate now includes whale_data
352
- if llm_analysis and llm_analysis.get('action') in ['BUY']: # Adjusted for SPOT only
353
- opportunity = {
354
- 'symbol': symbol, 'current_price': candidate.get('current_price', 0),
355
- 'decision': llm_analysis, 'enhanced_score': candidate.get('enhanced_final_score', 0),
356
- 'llm_confidence': llm_analysis.get('confidence_level', 0),
357
- 'strategy': llm_analysis.get('strategy', 'GENERIC'),
358
- 'analysis_timestamp': datetime.now().isoformat(),
359
- 'timeframes_count': timeframes_count, 'total_candles': total_candles
360
- }
361
  final_opportunities.append(opportunity)
362
  print(f" ✅ {symbol}: {llm_analysis.get('action')} - ثقة: {llm_analysis.get('confidence_level', 0):.2f}")
363
- else:
364
- action = llm_analysis.get('action', 'NO_DECISION') if llm_analysis else 'NO_RESPONSE'
365
- print(f" ⚠️ {symbol}: لا يوجد قرار تداول من النموذج الضخم ({action})")
366
  except Exception as e: print(f"❌ خطأ في تحليل النموذج الضخم لـ {candidate.get('symbol')}: {e}"); continue
367
 
368
  if final_opportunities:
369
  final_opportunities.sort(key=lambda x: (x['llm_confidence'] + x['enhanced_score']) / 2, reverse=True)
370
  print(f"\n🏆 النظام الطبقي اكتمل: {len(final_opportunities)} فرصة تداول")
371
- for i, opportunity in enumerate(final_opportunities[:5]):
372
- print(f" {i+1}. {opportunity['symbol']}: {opportunity['decision'].get('action')} - ثقة: {opportunity['llm_confidence']:.2f} - أطر: {opportunity['timeframes_count']}")
373
 
374
- # 12. سجل التدقيق (كما كان)
375
  try:
376
  top_10_detailed_summary = []
377
  for c in final_layer2_candidates:
378
- whale_summary = "Not Available"
379
- whale_data = c.get('whale_data') # Already preloaded
380
- if whale_data and whale_data.get('data_available'):
381
- signal = whale_data.get('trading_signal', {})
382
- action = signal.get('action', 'HOLD')
383
- confidence = signal.get('confidence', 0)
384
- reason_preview = signal.get('reason', 'N/A')[:75] + "..." if signal.get('reason') else 'N/A'
385
- whale_summary = f"Action: {action}, Conf: {confidence:.2f}, Alert: {signal.get('critical_alert', False)}, Reason: {reason_preview}"
386
- elif whale_data and whale_data.get('error'):
387
- whale_summary = f"Error: {whale_data['error'][:50]}..."
388
-
389
- top_10_detailed_summary.append({
390
- "symbol": c.get('symbol'), "score": c.get('enhanced_final_score', 0),
391
- "timeframes": f"{c.get('successful_timeframes', 'N/A')}/6",
392
- "whale_data_summary": whale_summary, "strategy": c.get('target_strategy', 'N/A'),
393
- "pattern": c.get('pattern_analysis', {}).get('pattern_detected', 'N/A'),
394
- })
395
  other_successful_candidates = layer2_candidates[target_count:]
396
  other_success_summary = [{"symbol": c['symbol'], "score": c.get('enhanced_final_score', 0), "timeframes": f"{c.get('successful_timeframes', 'N/A')}/6", "whale_data": "Available" if c.get('whale_data', {}).get('data_available') else ("Error" if c.get('whale_data', {}).get('error') else "Not Available")} for c in other_successful_candidates]
397
  low_score_summary = [{"symbol": c['symbol'], "score": c.get('enhanced_final_score', 0), "timeframes": f"{c.get('successful_timeframes', 'N/A')}/6", "whale_data": "Available" if c.get('whale_data', {}).get('data_available') else ("Error" if c.get('whale_data', {}).get('error') else "Not Available")} for c in all_low_score_candidates]
398
- audit_data = {
399
- "timestamp": datetime.now().isoformat(), "total_layer1_candidates": len(layer1_candidates),
400
- "total_processed_in_layer2": len(layer2_candidates) + len(all_low_score_candidates) + len(all_failed_candidates),
401
- "counts": {"sent_to_llm": len(final_layer2_candidates), "success_not_top_10": len(other_successful_candidates), "success_low_score": len(all_low_score_candidates), "failures": len(all_failed_candidates)},
402
- "top_candidates_for_llm": top_10_detailed_summary, "other_successful_candidates": other_success_summary,
403
- "low_score_candidates": low_score_summary, "failed_candidates": all_failed_candidates,
404
- }
405
  await r2_service_global.save_analysis_audit_log_async(audit_data)
406
  print(f"✅ تم حفظ سجل تدقيق التحليل في R2.")
407
  except Exception as audit_error: print(f"❌ فشل حفظ سجل تدقيق التحليل: {audit_error}"); traceback.print_exc()
@@ -427,21 +352,15 @@ async def re_analyze_open_trade_async(trade_data):
427
  ohlcv_data_list = []
428
  temp_queue = asyncio.Queue()
429
  await data_manager_global.stream_ohlcv_data([symbol], temp_queue)
430
- # Correctly drain the queue
431
  while True:
432
  try:
433
- batch = await asyncio.wait_for(temp_queue.get(), timeout=1.0) # Add timeout
434
- if batch is None: # Check for sentinel
435
- temp_queue.task_done()
436
- break
437
  ohlcv_data_list.extend(batch)
438
  temp_queue.task_done()
439
  except asyncio.TimeoutError:
440
- # Queue might be empty or producer finished without sentinel
441
- if temp_queue.empty(): break # Exit if empty after timeout
442
- except Exception as q_err:
443
- print(f"Error draining queue for re-analysis: {q_err}")
444
- break # Exit on other errors
445
 
446
  if not ohlcv_data_list: print(f"⚠️ فشل جلب بيانات إعادة التحليل لـ {symbol}"); return None
447
  ohlcv_data = ohlcv_data_list[0]
@@ -475,7 +394,7 @@ async def run_bot_cycle_async():
475
  print("🔄 بدء دورة التداول..."); await r2_service_global.save_system_logs_async({"cycle_started": True})
476
  if not r2_service_global.acquire_lock(): print("❌ فشل الحصول على القفل - تخطي الدورة"); return
477
 
478
- open_trades = [] # Define open_trades before try block
479
  try:
480
  open_trades = await trade_manager_global.get_open_trades(); print(f"📋 الصفقات المفتوحة: {len(open_trades)}")
481
  should_look_for_new_trade = len(open_trades) == 0
@@ -484,26 +403,15 @@ async def run_bot_cycle_async():
484
  trades_to_reanalyze = [t for t in open_trades if now >= datetime.fromisoformat(t.get('expected_target_time', now.isoformat()))]
485
  if trades_to_reanalyze:
486
  print(f"🔄 إعادة تحليل {len(trades_to_reanalyze)} صفقة")
487
- # Use gather for concurrent re-analysis, but process results sequentially
488
  reanalysis_results = await asyncio.gather(*[re_analyze_open_trade_async(trade) for trade in trades_to_reanalyze], return_exceptions=True)
489
-
490
  for i, result in enumerate(reanalysis_results):
491
- trade = trades_to_reanalyze[i] # Get corresponding trade
492
- if isinstance(result, Exception):
493
- print(f" فشل إعادة تحليل {trade.get('symbol')}: {result}")
494
- elif result and result['decision'].get('action') == "CLOSE_TRADE":
495
- print(f" إغلاق {trade.get('symbol')} بناءً على إعادة التحليل.")
496
- await trade_manager_global.close_trade(trade, result['current_price'], 'CLOSED_BY_REANALYSIS');
497
- should_look_for_new_trade = True # Now we can look for a new trade
498
- elif result and result['decision'].get('action') == "UPDATE_TRADE":
499
- print(f" ✅ تحديث {trade.get('symbol')} بناءً على إعادة التحليل.")
500
- await trade_manager_global.update_trade(trade, result['decision'])
501
- elif result: # Handle HOLD case (log or do nothing)
502
- print(f" ℹ️ الاحتفاظ بـ {trade.get('symbol')} بناءً على إعادة التحليل.")
503
- else: # Handle case where re-analysis returned None without error
504
- print(f" ⚠️ إعادة تحليل {trade.get('symbol')} لم تنتج قرارًا.")
505
 
506
- # Recalculate should_look_for_new_trade after potential closures
507
  current_open_trades_count = len(await trade_manager_global.get_open_trades())
508
  should_look_for_new_trade = current_open_trades_count == 0
509
 
@@ -515,19 +423,16 @@ async def run_bot_cycle_async():
515
  if best_opportunity: print(f"✅ فتح صفقة جديدة: {best_opportunity['symbol']}"); await trade_manager_global.open_trade( best_opportunity['symbol'], best_opportunity['decision'], best_opportunity['current_price'])
516
  else: print("❌ لم يتم العثور على فرص تداول مناسبة")
517
  else: print("❌ رأس المال غير كافي لفتح صفقات جديدة")
518
- else:
519
- print("ℹ️ يوجد صفقة مفتوحة بالفعل، تخطي البحث عن صفقة جديدة.")
520
-
521
  finally:
522
- if r2_service_global.lock_acquired: # Check if lock was acquired before releasing
523
- r2_service_global.release_lock()
524
- await r2_service_global.save_system_logs_async({ "cycle_completed": True, "open_trades": len(open_trades)}) # Use initial count for log consistency
525
  print("✅ اكتملت دورة التداول")
526
 
527
  except Exception as error:
528
- print(f"❌ Unhandled error in main cycle: {error}"); traceback.print_exc() # Print traceback
529
  await r2_service_global.save_system_logs_async({ "cycle_error": True, "error": str(error) });
530
- if r2_service_global and r2_service_global.lock_acquired: r2_service_global.release_lock() # Ensure lock release on error
531
 
532
  @asynccontextmanager
533
  async def lifespan(application: FastAPI):
@@ -543,15 +448,11 @@ async def lifespan(application: FastAPI):
543
  yield
544
  except Exception as error:
545
  print(f"❌ Application startup failed: {error}");
546
- traceback.print_exc() # Print traceback here as well
547
  if r2_service_global:
548
- await r2_service_global.save_system_logs_async({
549
- "application_startup_failed": True,
550
- "error": str(error)
551
- })
552
- # 🔴 --- الإصلاح: فصل raise إلى سطر جديد --- 🔴
553
  raise # Correct indentation
554
- # 🔴 --- نهاية الإصلاح --- 🔴
555
  finally:
556
  await cleanup_on_shutdown()
557
 
 
1
+ # app.py (محدث بإضافة Timeout لجلب الحيتان وإصلاح IndentationError)
2
  import os
3
  import traceback
4
  import signal
 
82
  symbol_whale_monitor_global = EnhancedWhaleMonitor(contracts_database, r2_service_global)
83
  state_manager.set_service_initialized('symbol_whale_monitor'); print(" ✅ مراقب الحيتان مهيأ")
84
  except Exception as e: print(f" ⚠️ فشل تهيئة مراقب الحيتان: {e}"); symbol_whale_monitor_global = None
85
+ print(" 🔄 تهيئة DataManager..."); data_manager_global = DataManager(contracts_database, symbol_whale_monitor_global); await data_manager_global.initialize(); state_manager.set_service_initialized('data_manager'); print(" ✅ DataManager مهيأ")
86
  print(" 🔄 تهيئة LLMService..."); llm_service_global = LLMService(); llm_service_global.r2_service = r2_service_global; state_manager.set_service_initialized('llm_service'); print(" ✅ LLMService مهيأة")
87
  print(" 🔄 تهيئة محلل المشاعر..."); sentiment_analyzer_global = SentimentAnalyzer(data_manager_global); state_manager.set_service_initialized('sentiment_analyzer'); print(" ✅ محلل المشاعر مهيأ")
88
  print(" 🔄 تهيئة محرك التعلم..."); learning_engine_global = LearningEngine(r2_service_global, data_manager_global); await learning_engine_global.initialize_enhanced(); state_manager.set_service_initialized('learning_engine'); print(" ✅ محرك التعلم مهيأ")
 
117
  """
118
  (معدلة) معالجة دفعة من الرموز بشكل متوازي وإرجاع نتائج مفصلة
119
  - تستخدم بيانات الحيتان المحملة مسبقًا
 
120
  """
121
  try:
 
 
 
122
  batch_tasks = []
123
  for symbol_data in batch:
124
+ task = asyncio.create_task(ml_processor.process_multiple_symbols_parallel([symbol_data], preloaded_whale_data))
 
125
  batch_tasks.append(task)
126
 
 
127
  batch_results_list_of_lists = await asyncio.gather(*batch_tasks, return_exceptions=True)
128
 
129
  successful_results = []
130
  low_score_results = []
131
  failed_results = []
132
 
 
133
  for i, result_list in enumerate(batch_results_list_of_lists):
134
  symbol = batch[i].get('symbol', 'unknown')
135
  if isinstance(result_list, Exception):
136
  failed_results.append({"symbol": symbol, "error": f"Task Execution Error: {str(result_list)}"})
137
  continue
138
 
 
139
  if result_list:
140
+ result = result_list[0]
141
+ if isinstance(result, dict):
142
  if result.get('enhanced_final_score', 0) > 0.4:
143
  successful_results.append(result)
144
  else:
145
  low_score_results.append(result)
146
  else:
 
147
  failed_results.append({"symbol": symbol, "error": f"ML processor returned invalid type: {type(result)}"})
148
  else:
 
149
  failed_results.append({"symbol": symbol, "error": "ML processing returned None or empty list"})
 
 
 
150
 
151
  return {'success': successful_results, 'low_score': low_score_results, 'failures': failed_results}
152
 
 
170
  all_failed_candidates = []
171
  final_layer2_candidates = []
172
  final_opportunities = []
173
+ preloaded_whale_data_dict = {}
174
 
175
  try:
176
  print("🎯 بدء النظام الطبقي المكون من 3 طبقات (مع فصل جلب الحيتان)...")
177
 
178
  if not await state_manager.wait_for_initialization(): print("❌ الخدمات غير مهيأة بالكامل"); return None
179
 
180
+ # الطبقة 1
181
  print("\n🔍 الطبقة 1: الفحص السريع (data_manager)...")
182
  layer1_candidates = await data_manager_global.layer1_rapid_screening()
183
  if not layer1_candidates: print("❌ لم يتم العثور على مرشحين في الطبقة 1"); return None
184
  print(f"✅ تم اختيار {len(layer1_candidates)} عملة للطبقة 2")
185
  layer1_symbols = [c['symbol'] for c in layer1_candidates]
186
 
187
+ # الطبقة 1.5: جلب بيانات الحيتان
188
  start_whale_fetch = time.time()
189
  print(f"\n🐋 الطبقة 1.5: بدء جلب بيانات الحيتان لـ {len(layer1_symbols)} عملة (بشكل غير معرقل)...")
 
 
190
  async def fetch_whale_data_task(symbols, results_dict):
191
  WHALE_FETCH_CONCURRENCY = 3
192
  semaphore = asyncio.Semaphore(WHALE_FETCH_CONCURRENCY)
193
  tasks = []
 
194
  async def get_data_with_semaphore(symbol):
195
  async with semaphore:
196
  try:
197
  data = await data_manager_global.get_whale_data_for_symbol(symbol)
198
+ if data: results_dict[symbol] = data
 
199
  except Exception as e:
200
  print(f" ❌ [Whale Fetch] فشل جلب بيانات الحيتان لـ {symbol}: {e}")
201
  results_dict[symbol] = {'data_available': False, 'error': str(e)}
202
+ for symbol in symbols: tasks.append(asyncio.create_task(get_data_with_semaphore(symbol)))
 
 
 
203
  await asyncio.gather(*tasks)
 
 
204
  whale_fetcher_task = asyncio.create_task(fetch_whale_data_task(layer1_symbols, preloaded_whale_data_dict))
205
  print(" ⏳ مهمة جلب بيانات الحيتان تعمل في الخلفية...")
 
206
 
207
+ # إعداد المنتج/المستهلك (OHLCV/ML)
208
  DATA_QUEUE_MAX_SIZE = 2
209
  ohlcv_data_queue = asyncio.Queue(maxsize=DATA_QUEUE_MAX_SIZE)
210
  ml_results_list = []
 
214
  total_batches = (len(layer1_candidates) + batch_size - 1) // batch_size
215
  print(f" 🚀 إعداد المنتج/المستهلك (OHLCV/ML): {total_batches} دفعة متوقعة (بحجم {batch_size})")
216
 
217
+ # وظيفة المستهلك (ML Consumer)
218
  async def ml_consumer_task(queue: asyncio.Queue, results_list: list, whale_data_store: dict):
219
  batch_num = 0
220
  while True:
221
  try:
222
  batch_data = await queue.get()
223
  if batch_data is None: queue.task_done(); print(" 🛑 [ML Consumer] تلقى إشارة التوقف."); break
 
224
  batch_num += 1
225
  print(f" 📬 [ML Consumer] استلم دفعة OHLCV {batch_num}/{total_batches} ({len(batch_data)} عملة)")
 
 
226
  batch_results_dict = await process_batch_parallel(
227
  batch_data, ml_processor, batch_num, total_batches, whale_data_store
228
  )
 
229
  results_list.append(batch_results_dict)
230
  queue.task_done()
231
+ print(f" ✅ [ML Consumer] أكمل معالجة ال��فعة {batch_num}/{total_batches}")
232
+ except Exception as e: print(f"❌ [ML Consumer] خطأ فادح: {e}"); traceback.print_exc(); queue.task_done()
 
 
233
 
234
+ # تشغيل المستهلك (ML Consumer) والمنتج (OHLCV Producer)
235
  print(" ▶️ [ML Consumer] بدء تشغيل مهمة المستهلك...")
236
  consumer_task = asyncio.create_task(ml_consumer_task(ohlcv_data_queue, ml_results_list, preloaded_whale_data_dict))
 
 
237
  print(" ▶️ [OHLCV Producer] بدء تشغيل مهمة المنتج (تدفق بيانات OHLCV)...")
238
+ producer_task = asyncio.create_task(data_manager_global.stream_ohlcv_data(layer1_symbols, ohlcv_data_queue))
 
 
239
 
240
+ # انتظار انتهاء المنتج والمستهلك
241
+ await producer_task; print(" ✅ [OHLCV Producer] أنهى جلب جميع بيانات OHLCV.")
242
+ await ohlcv_data_queue.put(None)
 
 
 
 
 
243
  await ohlcv_data_queue.join()
244
+ await consumer_task; print(" ✅ [ML Consumer] أنهى معالجة جميع الدفعات.")
 
245
 
246
+ # انتظار اكتمال مهمة جلب الحيتان (مع Timeout)
247
+ print(" ⏳ انتظار اكتمال مهمة جلب بيانات الحيتان (بحد أقصى للمهلة)...")
248
+ WHALE_FETCH_TIMEOUT_SECONDS = 180
249
+ try:
250
+ await asyncio.wait_for(whale_fetcher_task, timeout=WHALE_FETCH_TIMEOUT_SECONDS)
251
+ end_whale_fetch = time.time()
252
+ print(f" ✅ اكتمل جلب بيانات الحيتان في {end_whale_fetch - start_whale_fetch:.2f} ثانية. تم جلب/محاولة جلب بيانات لـ {len(preloaded_whale_data_dict)} عملة.")
253
+ except asyncio.TimeoutError:
254
+ end_whale_fetch = time.time()
255
+ print(f" ⚠️ انتهت مهلة انتظار جلب بيانات الحيتان ({WHALE_FETCH_TIMEOUT_SECONDS} ثانية)! تم جلب/محاولة جلب بيانات لـ {len(preloaded_whale_data_dict)} عملة حتى الآن.")
256
+ except Exception as whale_task_err:
257
+ end_whale_fetch = time.time()
258
+ print(f" ❌ حدث خطأ غير متوقع أثناء انتظار مهمة جلب الحيتان: {whale_task_err}")
259
+
260
+ # تجميع النتائج
261
  print("🔄 تجميع جميع النتائج...")
262
  for batch_result in ml_results_list:
263
  for success_item in batch_result['success']:
 
266
  if l1_data:
267
  success_item['reasons_for_candidacy'] = l1_data.get('reasons', [])
268
  success_item['layer1_score'] = l1_data.get('layer1_score', 0)
269
+ if symbol in preloaded_whale_data_dict: success_item['whale_data'] = preloaded_whale_data_dict[symbol]
270
+ elif 'whale_data' not in success_item: success_item['whale_data'] = {'data_available': False, 'reason': 'Fetch timed out or failed'}
 
 
271
  layer2_candidates.append(success_item)
272
  all_low_score_candidates.extend(batch_result['low_score'])
273
  all_failed_candidates.extend(batch_result['failures'])
274
 
275
  print(f"✅ اكتمل التحليل المتقدم: {len(layer2_candidates)} نجاح (عالي) | {len(all_low_score_candidates)} ن��اح (منخفض) | {len(all_failed_candidates)} فشل")
 
276
  if not layer2_candidates: print("❌ لم يتم العثور على مرشحين في الطبقة 2")
277
 
278
+ # الترتيب والفلترة
279
  layer2_candidates.sort(key=lambda x: x.get('enhanced_final_score', 0), reverse=True)
280
  target_count = min(10, len(layer2_candidates))
281
  final_layer2_candidates = layer2_candidates[:target_count]
 
283
  await r2_service_global.save_candidates_async(final_layer2_candidates)
284
  print("\n🏆 أفضل 10 عملات من الطبقة 2:")
285
  for i, candidate in enumerate(final_layer2_candidates):
286
+ score=candidate.get('enhanced_final_score',0); strategy=candidate.get('target_strategy','GENERIC'); mc_score=candidate.get('monte_carlo_probability'); pattern=candidate.get('pattern_analysis',{}).get('pattern_detected','no_pattern'); timeframes=candidate.get('successful_timeframes',0); symbol=candidate.get('symbol','UNKNOWN')
 
 
 
 
 
 
287
  print(f" {i+1}. {symbol}: 📊 {score:.3f} | الأطر: {timeframes}/6")
288
  if mc_score is not None: print(f" 🎯 مونت كارلو: {mc_score:.3f}")
289
  print(f" 🎯 استراتيجية: {strategy} | نمط: {pattern}")
290
  whale_data = candidate.get('whale_data')
291
+ if whale_data and whale_data.get('data_available'): signal = whale_data.get('trading_signal', {}); print(f" 🐋 حيتان: {signal.get('action', 'HOLD')} (ثقة: {signal.get('confidence', 0):.2f}){' ⚠️' if signal.get('critical_alert') else ''}")
292
+ elif whale_data and whale_data.get('error'): print(f" 🐋 حيتان: خطأ ({whale_data.get('error')[:50]}...)")
 
 
 
293
 
294
+ # الطبقة 3
295
  print("\n🧠 الطبقة 3: التحليل بالنموذج الضخم (LLMService)...")
296
  for candidate in final_layer2_candidates:
297
  try:
298
+ symbol = candidate['symbol']; print(f" 🤔 تحليل {symbol} بالنموذج الضخم...")
299
+ ohlcv_data = candidate.get('ohlcv');
 
300
  if not ohlcv_data: print(f" ⚠️ لا توجد بيانات شموع لـ {symbol}"); continue
301
  candidate['raw_ohlcv'] = ohlcv_data
302
+ timeframes_count = candidate.get('successful_timeframes', 0); total_candles = sum(len(data) for data in ohlcv_data.values()) if ohlcv_data else 0
 
303
  if total_candles < 30: print(f" ⚠️ بيانات شموع غير كافية لـ {symbol}: {total_candles} شمعة فقط"); continue
304
  print(f" 📊 إرسال {symbol} للنموذج: {total_candles} شمعة في {timeframes_count} إطار زمني")
305
+ llm_analysis = await llm_service_global.get_trading_decision(candidate)
306
+ if llm_analysis and llm_analysis.get('action') in ['BUY']:
307
+ opportunity={'symbol': symbol, 'current_price': candidate.get('current_price', 0), 'decision': llm_analysis, 'enhanced_score': candidate.get('enhanced_final_score', 0), 'llm_confidence': llm_analysis.get('confidence_level', 0), 'strategy': llm_analysis.get('strategy', 'GENERIC'), 'analysis_timestamp': datetime.now().isoformat(), 'timeframes_count': timeframes_count, 'total_candles': total_candles}
 
 
 
 
 
 
 
308
  final_opportunities.append(opportunity)
309
  print(f" ✅ {symbol}: {llm_analysis.get('action')} - ثقة: {llm_analysis.get('confidence_level', 0):.2f}")
310
+ else: action = llm_analysis.get('action', 'NO_DECISION') if llm_analysis else 'NO_RESPONSE'; print(f" ⚠️ {symbol}: لا يوجد قرار تداول من النموذج الضخم ({action})")
 
 
311
  except Exception as e: print(f"❌ خطأ في تحليل النموذج الضخم لـ {candidate.get('symbol')}: {e}"); continue
312
 
313
  if final_opportunities:
314
  final_opportunities.sort(key=lambda x: (x['llm_confidence'] + x['enhanced_score']) / 2, reverse=True)
315
  print(f"\n🏆 النظام الطبقي اكتمل: {len(final_opportunities)} فرصة تداول")
316
+ for i, opportunity in enumerate(final_opportunities[:5]): print(f" {i+1}. {opportunity['symbol']}: {opportunity['decision'].get('action')} - ثقة: {opportunity['llm_confidence']:.2f} - أطر: {opportunity['timeframes_count']}")
 
317
 
318
+ # سجل التدقيق
319
  try:
320
  top_10_detailed_summary = []
321
  for c in final_layer2_candidates:
322
+ whale_summary = "Not Available"; whale_data = c.get('whale_data')
323
+ if whale_data and whale_data.get('data_available'): signal = whale_data.get('trading_signal', {}); action = signal.get('action', 'HOLD'); confidence = signal.get('confidence', 0); reason_preview = signal.get('reason', 'N/A')[:75] + "..." if signal.get('reason') else 'N/A'; whale_summary = f"Action: {action}, Conf: {confidence:.2f}, Alert: {signal.get('critical_alert', False)}, Reason: {reason_preview}"
324
+ elif whale_data and whale_data.get('error'): whale_summary = f"Error: {whale_data['error'][:50]}..."
325
+ top_10_detailed_summary.append({ "symbol": c.get('symbol'), "score": c.get('enhanced_final_score', 0), "timeframes": f"{c.get('successful_timeframes', 'N/A')}/6", "whale_data_summary": whale_summary, "strategy": c.get('target_strategy', 'N/A'), "pattern": c.get('pattern_analysis', {}).get('pattern_detected', 'N/A'), })
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  other_successful_candidates = layer2_candidates[target_count:]
327
  other_success_summary = [{"symbol": c['symbol'], "score": c.get('enhanced_final_score', 0), "timeframes": f"{c.get('successful_timeframes', 'N/A')}/6", "whale_data": "Available" if c.get('whale_data', {}).get('data_available') else ("Error" if c.get('whale_data', {}).get('error') else "Not Available")} for c in other_successful_candidates]
328
  low_score_summary = [{"symbol": c['symbol'], "score": c.get('enhanced_final_score', 0), "timeframes": f"{c.get('successful_timeframes', 'N/A')}/6", "whale_data": "Available" if c.get('whale_data', {}).get('data_available') else ("Error" if c.get('whale_data', {}).get('error') else "Not Available")} for c in all_low_score_candidates]
329
+ audit_data = { "timestamp": datetime.now().isoformat(), "total_layer1_candidates": len(layer1_candidates), "total_processed_in_layer2": len(layer2_candidates) + len(all_low_score_candidates) + len(all_failed_candidates), "counts": {"sent_to_llm": len(final_layer2_candidates), "success_not_top_10": len(other_successful_candidates), "success_low_score": len(all_low_score_candidates), "failures": len(all_failed_candidates)}, "top_candidates_for_llm": top_10_detailed_summary, "other_successful_candidates": other_success_summary, "low_score_candidates": low_score_summary, "failed_candidates": all_failed_candidates, }
 
 
 
 
 
 
330
  await r2_service_global.save_analysis_audit_log_async(audit_data)
331
  print(f"✅ تم حفظ سجل تدقيق التحليل في R2.")
332
  except Exception as audit_error: print(f"❌ فشل حفظ سجل تدقيق التحليل: {audit_error}"); traceback.print_exc()
 
352
  ohlcv_data_list = []
353
  temp_queue = asyncio.Queue()
354
  await data_manager_global.stream_ohlcv_data([symbol], temp_queue)
 
355
  while True:
356
  try:
357
+ batch = await asyncio.wait_for(temp_queue.get(), timeout=1.0)
358
+ if batch is None: temp_queue.task_done(); break
 
 
359
  ohlcv_data_list.extend(batch)
360
  temp_queue.task_done()
361
  except asyncio.TimeoutError:
362
+ if temp_queue.empty(): break
363
+ except Exception as q_err: print(f"Error draining queue for re-analysis: {q_err}"); break
 
 
 
364
 
365
  if not ohlcv_data_list: print(f"⚠️ فشل جلب بيانات إعادة التحليل لـ {symbol}"); return None
366
  ohlcv_data = ohlcv_data_list[0]
 
394
  print("🔄 بدء دورة التداول..."); await r2_service_global.save_system_logs_async({"cycle_started": True})
395
  if not r2_service_global.acquire_lock(): print("❌ فشل الحصول على القفل - تخطي الدورة"); return
396
 
397
+ open_trades = []
398
  try:
399
  open_trades = await trade_manager_global.get_open_trades(); print(f"📋 الصفقات المفتوحة: {len(open_trades)}")
400
  should_look_for_new_trade = len(open_trades) == 0
 
403
  trades_to_reanalyze = [t for t in open_trades if now >= datetime.fromisoformat(t.get('expected_target_time', now.isoformat()))]
404
  if trades_to_reanalyze:
405
  print(f"🔄 إعادة تحليل {len(trades_to_reanalyze)} صفقة")
 
406
  reanalysis_results = await asyncio.gather(*[re_analyze_open_trade_async(trade) for trade in trades_to_reanalyze], return_exceptions=True)
 
407
  for i, result in enumerate(reanalysis_results):
408
+ trade = trades_to_reanalyze[i]
409
+ if isinstance(result, Exception): print(f" ❌ فشل إعادة تحليل {trade.get('symbol')}: {result}")
410
+ elif result and result['decision'].get('action') == "CLOSE_TRADE": print(f" إغلاق {trade.get('symbol')} بناءً على إعادة التحليل."); await trade_manager_global.close_trade(trade, result['current_price'], 'CLOSED_BY_REANALYSIS');
411
+ elif result and result['decision'].get('action') == "UPDATE_TRADE": print(f" ✅ تحديث {trade.get('symbol')} بناءً على إعادة التحليل."); await trade_manager_global.update_trade(trade, result['decision'])
412
+ elif result: print(f" ℹ️ الاحتفاظ بـ {trade.get('symbol')} بناءً على إعادة التحليل.")
413
+ else: print(f" ⚠️ إعادة تحليل {trade.get('symbol')} لم تنتج قرارًا.")
 
 
 
 
 
 
 
 
414
 
 
415
  current_open_trades_count = len(await trade_manager_global.get_open_trades())
416
  should_look_for_new_trade = current_open_trades_count == 0
417
 
 
423
  if best_opportunity: print(f"✅ فتح صفقة جديدة: {best_opportunity['symbol']}"); await trade_manager_global.open_trade( best_opportunity['symbol'], best_opportunity['decision'], best_opportunity['current_price'])
424
  else: print("❌ لم يتم العثور على فرص تداول مناسبة")
425
  else: print("❌ رأس المال غير كافي لفتح صفقات جديدة")
426
+ else: print("ℹ️ يوجد صفقة مفتوحة بالفعل، تخطي البحث عن صفقة جديدة.")
 
 
427
  finally:
428
+ if r2_service_global.lock_acquired: r2_service_global.release_lock()
429
+ await r2_service_global.save_system_logs_async({ "cycle_completed": True, "open_trades": len(open_trades)})
 
430
  print("✅ اكتملت دورة التداول")
431
 
432
  except Exception as error:
433
+ print(f"❌ Unhandled error in main cycle: {error}"); traceback.print_exc()
434
  await r2_service_global.save_system_logs_async({ "cycle_error": True, "error": str(error) });
435
+ if r2_service_global and r2_service_global.lock_acquired: r2_service_global.release_lock()
436
 
437
  @asynccontextmanager
438
  async def lifespan(application: FastAPI):
 
448
  yield
449
  except Exception as error:
450
  print(f"❌ Application startup failed: {error}");
451
+ traceback.print_exc()
452
  if r2_service_global:
453
+ await r2_service_global.save_system_logs_async({ "application_startup_failed": True, "error": str(error) })
454
+ # الإصلاح: فصل raise إلى سطر جديد
 
 
 
455
  raise # Correct indentation
 
456
  finally:
457
  await cleanup_on_shutdown()
458