Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# app.py (محدث
|
| 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]
|
| 149 |
-
if isinstance(result, 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 |
-
#
|
| 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 |
-
#
|
| 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] أكمل معالجة
|
| 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 |
-
# انتظار انتهاء المنتج
|
| 274 |
-
await producer_task
|
| 275 |
-
|
| 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 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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 |
-
#
|
| 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('
|
| 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 |
-
|
| 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 |
-
#
|
| 339 |
print("\n🧠 الطبقة 3: التحليل بالنموذج الضخم (LLMService)...")
|
| 340 |
for candidate in final_layer2_candidates:
|
| 341 |
try:
|
| 342 |
-
symbol = candidate['symbol']
|
| 343 |
-
|
| 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)
|
| 352 |
-
if llm_analysis and llm_analysis.get('action') in ['BUY']:
|
| 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 |
-
#
|
| 375 |
try:
|
| 376 |
top_10_detailed_summary = []
|
| 377 |
for c in final_layer2_candidates:
|
| 378 |
-
whale_summary = "Not Available"
|
| 379 |
-
whale_data
|
| 380 |
-
|
| 381 |
-
|
| 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)
|
| 434 |
-
if batch is None:
|
| 435 |
-
temp_queue.task_done()
|
| 436 |
-
break
|
| 437 |
ohlcv_data_list.extend(batch)
|
| 438 |
temp_queue.task_done()
|
| 439 |
except asyncio.TimeoutError:
|
| 440 |
-
|
| 441 |
-
|
| 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 = []
|
| 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]
|
| 492 |
-
if isinstance(result, Exception):
|
| 493 |
-
|
| 494 |
-
elif result and result['decision'].get('action') == "
|
| 495 |
-
|
| 496 |
-
|
| 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:
|
| 523 |
-
|
| 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()
|
| 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()
|
| 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()
|
| 547 |
if r2_service_global:
|
| 548 |
-
await r2_service_global.save_system_logs_async({
|
| 549 |
-
|
| 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 |
|