Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -190,8 +190,11 @@ async def monitor_market_async():
|
|
| 190 |
except Exception as e:
|
| 191 |
print(f"❌ فشل تشغيل مراقبة السوق: {e}")
|
| 192 |
|
|
|
|
|
|
|
|
|
|
| 193 |
async def process_batch_parallel(batch, ml_processor, batch_num, total_batches):
|
| 194 |
-
"""معالجة دفعة من الرموز بشكل متوازي"""
|
| 195 |
try:
|
| 196 |
print(f" 🔄 معالجة الدفعة {batch_num}/{total_batches} ({len(batch)} عملة)...")
|
| 197 |
|
|
@@ -204,20 +207,42 @@ async def process_batch_parallel(batch, ml_processor, batch_num, total_batches):
|
|
| 204 |
# انتظار انتهاء جميع مهام الدفعة الحالية
|
| 205 |
batch_results = await asyncio.gather(*batch_tasks, return_exceptions=True)
|
| 206 |
|
| 207 |
-
#
|
|
|
|
|
|
|
| 208 |
successful_results = []
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
if isinstance(result, Exception):
|
| 211 |
-
|
| 212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
successful_results.append(result)
|
|
|
|
|
|
|
|
|
|
| 214 |
|
| 215 |
-
print(f" ✅ اكتملت الدفعة {batch_num}: {len(successful_results)}
|
| 216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
|
| 218 |
except Exception as error:
|
| 219 |
print(f"❌ خطأ في معالجة الدفعة {batch_num}: {error}")
|
| 220 |
-
|
|
|
|
| 221 |
|
| 222 |
async def run_3_layer_analysis():
|
| 223 |
"""
|
|
@@ -226,6 +251,15 @@ async def run_3_layer_analysis():
|
|
| 226 |
الطبقة 2: MLProcessor - التحليل المتقدم
|
| 227 |
الطبقة 3: LLMService - النموذج الضخم
|
| 228 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
try:
|
| 230 |
print("🎯 بدء النظام الطبقي المكون من 3 طبقات...")
|
| 231 |
|
|
@@ -294,21 +328,28 @@ async def run_3_layer_analysis():
|
|
| 294 |
task = asyncio.create_task(process_batch_parallel(batch, ml_processor, i+1, total_batches))
|
| 295 |
batch_tasks.append(task)
|
| 296 |
|
| 297 |
-
#
|
| 298 |
-
|
|
|
|
|
|
|
| 299 |
|
| 300 |
# دمج جميع النتائج
|
| 301 |
layer2_candidates = []
|
| 302 |
-
|
| 303 |
-
|
| 304 |
|
| 305 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
|
| 307 |
if not layer2_candidates:
|
| 308 |
print("❌ لم يتم العثور على مرشحين في الطبقة 2")
|
| 309 |
-
|
| 310 |
|
| 311 |
-
# ترتيب المرشحين حسب الدرجة المحسنة وأخذ أقوى 10
|
| 312 |
layer2_candidates.sort(key=lambda x: x.get('enhanced_final_score', 0), reverse=True)
|
| 313 |
target_count = min(10, len(layer2_candidates))
|
| 314 |
final_layer2_candidates = layer2_candidates[:target_count]
|
|
@@ -399,22 +440,122 @@ async def run_3_layer_analysis():
|
|
| 399 |
print(f"❌ خطأ في تحليل النموذج الضخم لـ {candidate.get('symbol')}: {e}")
|
| 400 |
continue
|
| 401 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
if not final_opportunities:
|
| 403 |
print("❌ لم يتم العثور على فرص تداول مناسبة")
|
| 404 |
return None
|
| 405 |
|
| 406 |
-
# ترتيب الفرص النهائية حسب الثقة والدرجة
|
| 407 |
-
final_opportunities.sort(key=lambda x: (x['llm_confidence'] + x['enhanced_score']) / 2, reverse=True)
|
| 408 |
-
|
| 409 |
-
print(f"\n🏆 النظام الطبقي اكتمل: {len(final_opportunities)} فرصة تداول")
|
| 410 |
-
for i, opportunity in enumerate(final_opportunities[:5]):
|
| 411 |
-
print(f" {i+1}. {opportunity['symbol']}: {opportunity['decision'].get('action')} - ثقة: {opportunity['llm_confidence']:.2f} - أطر: {opportunity['timeframes_count']}")
|
| 412 |
-
|
| 413 |
return final_opportunities[0] if final_opportunities else None
|
| 414 |
|
| 415 |
except Exception as error:
|
| 416 |
print(f"❌ خطأ في النظام الطبقي: {error}")
|
| 417 |
traceback.print_exc()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
return None
|
| 419 |
|
| 420 |
async def re_analyze_open_trade_async(trade_data):
|
|
|
|
| 190 |
except Exception as e:
|
| 191 |
print(f"❌ فشل تشغيل مراقبة السوق: {e}")
|
| 192 |
|
| 193 |
+
#
|
| 194 |
+
# 🔴 تم التعديل: الدالة الآن ترجع قاموساً مفصلاً بدلاً من قائمة واحدة
|
| 195 |
+
#
|
| 196 |
async def process_batch_parallel(batch, ml_processor, batch_num, total_batches):
|
| 197 |
+
"""معالجة دفعة من الرموز بشكل متوازي وإرجاع نتائج مفصلة"""
|
| 198 |
try:
|
| 199 |
print(f" 🔄 معالجة الدفعة {batch_num}/{total_batches} ({len(batch)} عملة)...")
|
| 200 |
|
|
|
|
| 207 |
# انتظار انتهاء جميع مهام الدفعة الحالية
|
| 208 |
batch_results = await asyncio.gather(*batch_tasks, return_exceptions=True)
|
| 209 |
|
| 210 |
+
#
|
| 211 |
+
# 🔴 تم التعديل: تصفية النتائج إلى ثلاث فئات
|
| 212 |
+
#
|
| 213 |
successful_results = []
|
| 214 |
+
low_score_results = []
|
| 215 |
+
failed_results = []
|
| 216 |
+
|
| 217 |
+
for i, result in enumerate(batch_results):
|
| 218 |
+
symbol = batch[i].get('symbol', 'unknown') # جلب الرمز من بيانات الدفعة الأصلية
|
| 219 |
+
|
| 220 |
if isinstance(result, Exception):
|
| 221 |
+
# فشل على مستوى المهمة (مثل Timeout)
|
| 222 |
+
failed_results.append({"symbol": symbol, "error": f"Task Execution Error: {str(result)}"})
|
| 223 |
+
elif result is None:
|
| 224 |
+
# فشل المعالجة داخل ML.py (سيرجع None)
|
| 225 |
+
failed_results.append({"symbol": symbol, "error": "ML.py processing returned None (Check logs for internal error)"})
|
| 226 |
+
elif result.get('enhanced_final_score', 0) > 0.4:
|
| 227 |
+
# نجاح - درجة عالية
|
| 228 |
successful_results.append(result)
|
| 229 |
+
else:
|
| 230 |
+
# نجاح - درجة منخفضة
|
| 231 |
+
low_score_results.append(result)
|
| 232 |
|
| 233 |
+
print(f" ✅ اكتملت الدفعة {batch_num}: {len(successful_results)} نجاح | {len(low_score_results)} منخفض | {len(failed_results)} فشل")
|
| 234 |
+
|
| 235 |
+
# إرجاع قاموس مفصل
|
| 236 |
+
return {
|
| 237 |
+
'success': successful_results,
|
| 238 |
+
'low_score': low_score_results,
|
| 239 |
+
'failures': failed_results
|
| 240 |
+
}
|
| 241 |
|
| 242 |
except Exception as error:
|
| 243 |
print(f"❌ خطأ في معالجة الدفعة {batch_num}: {error}")
|
| 244 |
+
# إرجاع هيكل فارغ في حالة فشل الدفعة بالكامل
|
| 245 |
+
return {'success': [], 'low_score': [], 'failures': []}
|
| 246 |
|
| 247 |
async def run_3_layer_analysis():
|
| 248 |
"""
|
|
|
|
| 251 |
الطبقة 2: MLProcessor - التحليل المتقدم
|
| 252 |
الطبقة 3: LLMService - النموذج الضخم
|
| 253 |
"""
|
| 254 |
+
|
| 255 |
+
# 🔴 تعريف متغيرات السجل في بداية الدالة
|
| 256 |
+
layer1_candidates = []
|
| 257 |
+
layer2_candidates = []
|
| 258 |
+
all_low_score_candidates = []
|
| 259 |
+
all_failed_candidates = []
|
| 260 |
+
final_layer2_candidates = []
|
| 261 |
+
final_opportunities = []
|
| 262 |
+
|
| 263 |
try:
|
| 264 |
print("🎯 بدء النظام الطبقي المكون من 3 طبقات...")
|
| 265 |
|
|
|
|
| 328 |
task = asyncio.create_task(process_batch_parallel(batch, ml_processor, i+1, total_batches))
|
| 329 |
batch_tasks.append(task)
|
| 330 |
|
| 331 |
+
#
|
| 332 |
+
# 🔴 تم التعديل: تجميع النتائج المفصلة
|
| 333 |
+
#
|
| 334 |
+
batch_results_list = await asyncio.gather(*batch_tasks)
|
| 335 |
|
| 336 |
# دمج جميع النتائج
|
| 337 |
layer2_candidates = []
|
| 338 |
+
all_low_score_candidates = []
|
| 339 |
+
all_failed_candidates = []
|
| 340 |
|
| 341 |
+
for batch_result in batch_results_list:
|
| 342 |
+
layer2_candidates.extend(batch_result['success'])
|
| 343 |
+
all_low_score_candidates.extend(batch_result['low_score'])
|
| 344 |
+
all_failed_candidates.extend(batch_result['failures'])
|
| 345 |
+
|
| 346 |
+
print(f"✅ اكتمل التحليل المتقدم: {len(layer2_candidates)} نجاح (عالي) | {len(all_low_score_candidates)} نجاح (منخفض) | {len(all_failed_candidates)} فشل")
|
| 347 |
|
| 348 |
if not layer2_candidates:
|
| 349 |
print("❌ لم يتم العثور على مرشحين في الطبقة 2")
|
| 350 |
+
# 🔴 استمرار لتسجيل السجل
|
| 351 |
|
| 352 |
+
# ترتيب المرشحين (الناجحين فقط) حسب الدرجة المحسنة وأخذ أقوى 10
|
| 353 |
layer2_candidates.sort(key=lambda x: x.get('enhanced_final_score', 0), reverse=True)
|
| 354 |
target_count = min(10, len(layer2_candidates))
|
| 355 |
final_layer2_candidates = layer2_candidates[:target_count]
|
|
|
|
| 440 |
print(f"❌ خطأ في تحليل النموذج الضخم لـ {candidate.get('symbol')}: {e}")
|
| 441 |
continue
|
| 442 |
|
| 443 |
+
if final_opportunities:
|
| 444 |
+
# ترتيب الفرص النهائية حسب الثقة والدرجة
|
| 445 |
+
final_opportunities.sort(key=lambda x: (x['llm_confidence'] + x['enhanced_score']) / 2, reverse=True)
|
| 446 |
+
|
| 447 |
+
print(f"\n🏆 النظام الطبقي اكتمل: {len(final_opportunities)} فرصة تداول")
|
| 448 |
+
for i, opportunity in enumerate(final_opportunities[:5]):
|
| 449 |
+
print(f" {i+1}. {opportunity['symbol']}: {opportunity['decision'].get('action')} - ثقة: {opportunity['llm_confidence']:.2f} - أطر: {opportunity['timeframes_count']}")
|
| 450 |
+
|
| 451 |
+
#
|
| 452 |
+
# 🔴 --- بدء سجل تدقيق التحليل ---
|
| 453 |
+
#
|
| 454 |
+
try:
|
| 455 |
+
# 1. ملخص الـ 10 الأوائل (لـ LLM)
|
| 456 |
+
top_10_detailed_summary = []
|
| 457 |
+
for c in final_layer2_candidates: # هذه هي قائمة الـ 10 الأوائل
|
| 458 |
+
whale_summary = "Not Available"
|
| 459 |
+
whale_data = c.get('whale_data')
|
| 460 |
+
if whale_data and whale_data.get('data_available'):
|
| 461 |
+
signal = whale_data.get('trading_signal', {})
|
| 462 |
+
action = signal.get('action', 'HOLD')
|
| 463 |
+
confidence = signal.get('confidence', 0)
|
| 464 |
+
reason_preview = signal.get('reason', 'N/A')[:75] + "..." if signal.get('reason') else 'N/A'
|
| 465 |
+
whale_summary = f"Action: {action}, Conf: {confidence:.2f}, Alert: {signal.get('critical_alert', False)}, Reason: {reason_preview}"
|
| 466 |
+
|
| 467 |
+
top_10_detailed_summary.append({
|
| 468 |
+
"symbol": c.get('symbol'),
|
| 469 |
+
"score": c.get('enhanced_final_score', 0),
|
| 470 |
+
"timeframes": f"{c.get('successful_timeframes', 'N/A')}/6",
|
| 471 |
+
"whale_data_summary": whale_summary,
|
| 472 |
+
"strategy": c.get('target_strategy', 'N/A'),
|
| 473 |
+
"pattern": c.get('pattern_analysis', {}).get('pattern_detected', 'N/A'),
|
| 474 |
+
})
|
| 475 |
+
|
| 476 |
+
# 2. ملخص باقي الناجحين (الذين لم يتم إرسالهم للنموذج)
|
| 477 |
+
other_successful_candidates = layer2_candidates[target_count:]
|
| 478 |
+
other_success_summary = [
|
| 479 |
+
{
|
| 480 |
+
"symbol": c['symbol'],
|
| 481 |
+
"score": c.get('enhanced_final_score', 0),
|
| 482 |
+
"timeframes": f"{c.get('successful_timeframes', 'N/A')}/6",
|
| 483 |
+
"whale_data": "Available" if c.get('whale_data', {}).get('data_available') else "Not Available"
|
| 484 |
+
}
|
| 485 |
+
for c in other_successful_candidates
|
| 486 |
+
]
|
| 487 |
+
|
| 488 |
+
# 3. ملخص الدرجات المنخفضة (نجاح < 0.4)
|
| 489 |
+
low_score_summary = [
|
| 490 |
+
{
|
| 491 |
+
"symbol": c['symbol'],
|
| 492 |
+
"score": c.get('enhanced_final_score', 0),
|
| 493 |
+
"timeframes": f"{c.get('successful_timeframes', 'N/A')}/6",
|
| 494 |
+
"whale_data": "Available" if c.get('whale_data', {}).get('data_available') else "Not Available"
|
| 495 |
+
}
|
| 496 |
+
for c in all_low_score_candidates
|
| 497 |
+
]
|
| 498 |
+
|
| 499 |
+
# 4. تجميع السجل النهائي
|
| 500 |
+
audit_data = {
|
| 501 |
+
"timestamp": datetime.now().isoformat(),
|
| 502 |
+
"total_layer1_candidates": len(layer1_candidates),
|
| 503 |
+
"total_processed_in_layer2": len(layer2_candidates) + len(all_low_score_candidates) + len(all_failed_candidates),
|
| 504 |
+
"counts": {
|
| 505 |
+
"sent_to_llm": len(final_layer2_candidates),
|
| 506 |
+
"success_not_top_10": len(other_successful_candidates),
|
| 507 |
+
"success_low_score": len(all_low_score_candidates),
|
| 508 |
+
"failures": len(all_failed_candidates)
|
| 509 |
+
},
|
| 510 |
+
|
| 511 |
+
"top_candidates_for_llm": top_10_detailed_summary,
|
| 512 |
+
"other_successful_candidates": other_success_summary,
|
| 513 |
+
"low_score_candidates": low_score_summary,
|
| 514 |
+
"failed_candidates": all_failed_candidates, # {"symbol": ..., "error": ...}
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
# 5. حفظ السجل
|
| 518 |
+
await r2_service_global.save_analysis_audit_log_async(audit_data)
|
| 519 |
+
print(f"✅ تم حفظ سجل تدقيق التحليل في R2.")
|
| 520 |
+
|
| 521 |
+
except Exception as audit_error:
|
| 522 |
+
print(f"❌ فشل حفظ سجل تدقيق التحليل: {audit_error}")
|
| 523 |
+
traceback.print_exc()
|
| 524 |
+
#
|
| 525 |
+
# 🔴 --- نهاية سجل تدقيق التحليل ---
|
| 526 |
+
#
|
| 527 |
+
|
| 528 |
if not final_opportunities:
|
| 529 |
print("❌ لم يتم العثور على فرص تداول مناسبة")
|
| 530 |
return None
|
| 531 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 532 |
return final_opportunities[0] if final_opportunities else None
|
| 533 |
|
| 534 |
except Exception as error:
|
| 535 |
print(f"❌ خطأ في النظام الطبقي: {error}")
|
| 536 |
traceback.print_exc()
|
| 537 |
+
|
| 538 |
+
# 🔴 تسجيل السجل حتى في حالة الفشل
|
| 539 |
+
try:
|
| 540 |
+
audit_data = {
|
| 541 |
+
"timestamp": datetime.now().isoformat(),
|
| 542 |
+
"status": "FAILED",
|
| 543 |
+
"error": str(error),
|
| 544 |
+
"traceback": traceback.format_exc(),
|
| 545 |
+
"total_layer1_candidates": len(layer1_candidates),
|
| 546 |
+
"counts": {
|
| 547 |
+
"sent_to_llm": 0,
|
| 548 |
+
"success_not_top_10": len(layer2_candidates[target_count:]) if 'target_count' in locals() else 0,
|
| 549 |
+
"success_low_score": len(all_low_score_candidates),
|
| 550 |
+
"failures": len(all_failed_candidates)
|
| 551 |
+
},
|
| 552 |
+
"failed_candidates": all_failed_candidates
|
| 553 |
+
}
|
| 554 |
+
await r2_service_global.save_analysis_audit_log_async(audit_data)
|
| 555 |
+
print("⚠️ تم حفظ سجل تدقيق جزئي بعد الفشل.")
|
| 556 |
+
except Exception as audit_fail_error:
|
| 557 |
+
print(f"❌ فشل حفظ سجل التدقيق أثناء معالجة خطأ آخر: {audit_fail_error}")
|
| 558 |
+
|
| 559 |
return None
|
| 560 |
|
| 561 |
async def re_analyze_open_trade_async(trade_data):
|