Riy777 commited on
Commit
cdb0782
·
verified ·
1 Parent(s): 109ff06

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +215 -761
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py (Updated to V8.7 - MC Expected Return Fix)
2
  import os
3
  import traceback
4
  import signal
@@ -10,59 +10,40 @@ import time
10
  from contextlib import asynccontextmanager
11
  from fastapi import FastAPI, HTTPException
12
  from datetime import datetime
13
- from typing import List, Dict, Any
14
 
 
15
  try:
16
  from r2 import R2Service
17
  from LLM import LLMService
18
- from data_manager import DataManager
19
  from ml_engine.processor import MLProcessor
20
  from learning_hub.hub_manager import LearningHubManager
21
- from sentiment_news import SentimentAnalyzer, NewsFetcher # (V8.1) استيراد NewsFetcher
22
  from trade_manager import TradeManager
23
- from ml_engine.monte_carlo import _sanitize_results_for_json
24
-
25
- from helpers import safe_float_conversion, validate_candidate_data_enhanced
26
  except ImportError as e:
27
- print(f"❌ خطأ في استيراد الوحدات: {e}")
28
- if "ccxt.async_support" in str(e) or "ccxtpro" in str(e):
29
- print("🚨 خطأ فادح: تأكد من أن 'ccxt' (الإصدار 4+) مثبت وأن 'ccxt-pro' محذوف.")
30
  sys.exit(1)
31
 
32
- # (V8.1) استيراد VADER
33
- try:
34
- from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
35
- VADER_ANALYZER = SentimentIntensityAnalyzer()
36
- print("✅ تم تحميل VADER Sentiment Analyzer بنجاح")
37
- except ImportError:
38
- print("❌❌ فشل استيراد VADER. درجة الأخبار ستكون معطلة. ❌❌")
39
- print(" قم بتثبيتها باستخدام: pip install vaderSentiment")
40
- VADER_ANALYZER = None
41
-
42
- # المتغيرات العالمية
43
  r2_service_global = None
44
  data_manager_global = None
45
  llm_service_global = None
46
  learning_hub_global = None
47
  trade_manager_global = None
48
- sentiment_analyzer_global = None
49
- symbol_whale_monitor_global = None
50
- news_fetcher_global = None # (V8.1) إضافة NewsFetcher
51
 
52
  MARKET_STATE_OK = True
53
 
54
-
55
  class StateManager:
56
- # ... (لا تغيير في هذا الكلاس) ...
57
  def __init__(self):
58
  self.market_analysis_lock = asyncio.Lock()
59
  self.trade_analysis_lock = asyncio.Lock()
60
  self.initialization_complete = False
61
  self.initialization_error = None
62
  self.services_initialized = {
63
- 'r2_service': False, 'data_manager': False, 'llm_service': False,
64
- 'learning_hub': False, 'trade_manager': False, 'sentiment_analyzer': False,
65
- 'symbol_whale_monitor': False, 'news_fetcher': False # (V8.1)
66
  }
67
 
68
  async def wait_for_initialization(self, timeout=60):
@@ -77,7 +58,7 @@ class StateManager:
77
  self.services_initialized[service_name] = True
78
  if all(self.services_initialized.values()):
79
  self.initialization_complete = True
80
- print("🎯 جميع الخدمات مهيأة بالكامل")
81
 
82
  def set_initialization_error(self, error):
83
  self.initialization_error = error
@@ -86,779 +67,252 @@ class StateManager:
86
  state_manager = StateManager()
87
 
88
  async def initialize_services():
89
- """تهيئة جميع الخدمات بشكل منفصل"""
90
  global r2_service_global, data_manager_global, llm_service_global
91
- global learning_hub_global, trade_manager_global, sentiment_analyzer_global
92
- global symbol_whale_monitor_global, news_fetcher_global, VADER_ANALYZER # (V8.1)
93
  try:
94
- # 🔴 --- START OF CHANGE (V7.0) --- 🔴
95
- print("🚀 بدء تهيئة الخدمات (بنية Sentry الجديدة V5.9)...")
96
- # 🔴 --- END OF CHANGE --- 🔴
97
- print(" 🔄 تهيئة R2Service..."); r2_service_global = R2Service(); state_manager.set_service_initialized('r2_service'); print(" ✅ R2Service مهيأة")
98
- print(" 🔄 جلب قاعدة بيانات العقود..."); contracts_database = await r2_service_global.load_contracts_db_async(); print(f" ✅ تم تحميل {len(contracts_database)} عقد")
99
-
100
- print(" 🔄 تهيئة مراقب الحيتان (Layer 1 Data)...");
101
- try:
102
- from whale_monitor.core import EnhancedWhaleMonitor
103
-
104
- symbol_whale_monitor_global = EnhancedWhaleMonitor(contracts_database, r2_service_global)
105
- state_manager.set_service_initialized('symbol_whale_monitor'); print(" ✅ مراقب الحيتان مهيأ")
106
 
107
- except Exception as e:
108
- print(f" ⚠️ فشل تهيئة مراقب الحيتان: {e}");
109
- traceback.print_exc()
110
- symbol_whale_monitor_global = None
111
- state_manager.set_service_initialized('symbol_whale_monitor');
112
- print(" ℹ️ مراقبة الحيتان معطلة. استمرار التهيئة...")
113
-
114
- # (V8-MODIFICATION) تمرير r2_service_global إلى DataManager
115
- print(" 🔄 تهيئة DataManager (Layer 1 Data)...");
116
- data_manager_global = DataManager(contracts_database, symbol_whale_monitor_global, r2_service_global)
117
- await data_manager_global.initialize();
118
- state_manager.set_service_initialized('data_manager');
119
- print(" ✅ DataManager مهيأ (ومحرك الأنماط V8 مُحمّل)")
120
-
121
- # (ربط DataManager بـ WhaleMonitor لحل الاعتمادية الدائرية للسعر)
122
- if symbol_whale_monitor_global:
123
- symbol_whale_monitor_global.data_manager = data_manager_global
124
- print(" ✅ [Whale Link] تم ربط DataManager بـ WhaleMonitor.")
125
-
126
- print(" 🔄 تهيئة LLMService (Layer 1 Brain)...");
127
- llm_service_global = LLMService();
128
- llm_service_global.r2_service = r2_service_global;
129
-
130
- print(" 🔄 تهيئة محلل المشاعر (Layer 1 Data)...");
131
- sentiment_analyzer_global = SentimentAnalyzer(data_manager_global);
132
- state_manager.set_service_initialized('sentiment_analyzer');
133
- print(" ✅ محلل المشاعر مهيأ")
134
 
135
- # (V8.1) تهيئة NewsFetcher
136
- print(" 🔄 تهيئة NewsFetcher (Layer 1 Data)...");
137
- news_fetcher_global = NewsFetcher()
138
- state_manager.set_service_initialized('news_fetcher');
139
- print(" ✅ NewsFetcher (V8.1) مهيأ")
140
 
141
- # 🔴 --- START OF CHANGE (V8.5 - Vader Injection) --- 🔴
142
- # (تمرير NewsFetcher و VADER إلى LLMService - مهم لإعادة التحليل)
143
- llm_service_global.news_fetcher = news_fetcher_global
144
- llm_service_global.vader_analyzer = VADER_ANALYZER # (هذا هو السطر المضاف)
145
- print(" ✅ [LLM Link] تم ربط NewsFetcher و VaderAnalyzer بـ LLMService.")
146
- # 🔴 --- END OF CHANGE --- 🔴
147
-
148
- print(" 🔄 تهيئة محور التعلم (Hub)...");
149
- learning_hub_global = LearningHubManager(
150
- r2_service=r2_service_global,
151
- llm_service=llm_service_global,
152
- data_manager=data_manager_global
153
- )
154
  await learning_hub_global.initialize()
155
- state_manager.set_service_initialized('learning_hub');
156
- print(" ✅ محور التعلم (Hub) مهيأ")
157
-
158
- llm_service_global.learning_hub = learning_hub_global
159
- state_manager.set_service_initialized('llm_service');
160
- print(" ✅ LLMService مربوط بمحور التعلم")
 
 
 
 
 
161
 
162
- print(" 🔄 تهيئة مدير الصفقات (Layer 2 Sentry + Layer 3 Executor)...");
163
-
164
- # (تمرير دالة الدورة كـ "رد نداء" ليتم استدعاؤها بعد إغلاق الصفقة)
165
  trade_manager_global = TradeManager(
166
- r2_service=r2_service_global,
167
- learning_hub=learning_hub_global,
168
- data_manager=data_manager_global,
169
- state_manager=state_manager,
170
- callback_on_close=run_bot_cycle_async
171
  )
172
-
173
  await trade_manager_global.initialize_sentry_exchanges()
174
- state_manager.set_service_initialized('trade_manager');
175
- print(" ✅ مدير الصفقات (Sentry/Executor) مهيأ")
176
 
177
- print("🎯 اكتملت تهيئة جميع الخدمات بنجاح"); return True
178
- except Exception as e: error_msg = f"فشل تهيئة الخدمات: {str(e)}"; print(f"❌ {error_msg}"); state_manager.set_initialization_error(error_msg); return False
 
 
 
 
179
 
180
  async def monitor_market_async():
181
- # ... (لا تغيير في هذه الدالة) ...
182
- global data_manager_global, sentiment_analyzer_global, MARKET_STATE_OK
183
  try:
184
- if not await state_manager.wait_for_initialization(): print("❌ فشل تهيئة الخدمات - إيقاف مراقبة السوق"); return
185
  while True:
186
  try:
187
  async with state_manager.market_analysis_lock:
188
- market_context = await sentiment_analyzer_global.get_market_sentiment()
 
189
 
190
- if not market_context: MARKET_STATE_OK = True; await asyncio.sleep(60); continue
191
-
192
- bitcoin_sentiment = market_context.get('btc_sentiment')
193
- fear_greed_index = market_context.get('fear_and_greed_index')
194
- should_halt_trading, halt_reason = False, ""
195
- if bitcoin_sentiment == 'BEARISH' and (fear_greed_index is not None and fear_greed_index < 30): should_halt_trading, halt_reason = True, "ظروف سوق هابطة"
196
-
197
- if should_halt_trading:
198
- MARKET_STATE_OK = False;
199
- await r2_service_global.save_system_logs_async({"market_halt": True, "reason": halt_reason})
200
  else:
201
- if not MARKET_STATE_OK: print("✅ تحسنت ظروف السوق. استئناف العمليات العادية.")
202
- MARKET_STATE_OK = True
203
-
204
- await asyncio.sleep(60)
205
- except Exception as error:
206
- print(f"❌ خطأ أثناء مراقبة السوق: {error}");
207
- MARKET_STATE_OK = True;
208
- await asyncio.sleep(60)
209
- except Exception as e: print(f"❌ فشل تشغيل مراقبة السوق: {e}")
210
-
211
-
212
- async def run_periodic_distillation():
213
- # ... (لا تغيير في هذه الدالة) ...
214
- print("background task: Periodic Distillation (Curator) scheduled.")
215
- await asyncio.sleep(300)
216
-
217
- while True:
218
- try:
219
- if not await state_manager.wait_for_initialization():
220
  await asyncio.sleep(60)
221
- continue
222
 
223
- print("🔄 [Scheduler] Running periodic distillation check...")
224
- await learning_hub_global.run_distillation_check()
225
- await asyncio.sleep(6 * 60 * 60)
 
 
226
 
227
- except Exception as e:
228
- print(f"❌ [Scheduler] Error in periodic distillation task: {e}")
229
- traceback.print_exc()
230
- await asyncio.sleep(60 * 60)
231
-
232
- async def process_batch_parallel(batch, ml_processor, batch_num, total_batches, preloaded_whale_data):
233
- # ... (لا تغيير في هذه الدالة) ...
234
  try:
235
- batch_tasks = []
236
- for symbol_data in batch:
237
- task = asyncio.create_task(ml_processor.process_multiple_symbols_parallel([symbol_data], preloaded_whale_data))
238
- batch_tasks.append(task)
239
-
240
- batch_results_list_of_lists = await asyncio.gather(*batch_tasks, return_exceptions=True)
241
-
242
- successful_results = []
243
- low_score_results = []
244
- failed_results = []
245
-
246
- for i, result_list in enumerate(batch_results_list_of_lists):
247
- symbol = batch[i].get('symbol', 'unknown')
248
- if isinstance(result_list, Exception):
249
- failed_results.append({"symbol": symbol, "error": f"Task Execution Error: {str(result_list)}"})
250
- continue
251
-
252
- if result_list:
253
- result = result_list[0]
254
- if isinstance(result, dict):
255
- if result.get('enhanced_final_score', 0) > 0.4:
256
- successful_results.append(result)
257
- else:
258
- low_score_results.append(result)
259
- else:
260
- failed_results.append({"symbol": symbol, "error": f"ML processor returned invalid type: {type(result)}"})
261
- else:
262
- failed_results.append({"symbol": symbol, "error": "ML processing returned None or empty list"})
263
-
264
- return {'success': successful_results, 'low_score': low_score_results, 'failures': failed_results}
265
-
266
- except Exception as error:
267
- print(f"❌ [Consumer] Error processing batch {batch_num}: {error}")
268
- return {'success': [], 'low_score': [], 'failures': []}
269
-
270
-
271
- async def run_3_layer_analysis_explorer() -> List[Dict[str, Any]]:
272
- """
273
- (معدل V8.7) - إصلاح العائد المتوقع (Expected Return)
274
- """
275
- layer1_candidates = []
276
- layer2_candidates = []
277
- final_layer2_candidates = []
278
- watchlist_candidates = []
279
-
280
- preloaded_whale_data_dict = {}
281
-
282
- try:
283
- print("🎯 Starting Explorer Analysis (Layer 1)...")
284
-
285
- if not await state_manager.wait_for_initialization():
286
- print("❌ Services not fully initialized (Explorer)"); return []
287
-
288
- # (V8.1) التأكد من تهيئة VADER و NewsFetcher
289
- if not VADER_ANALYZER or not news_fetcher_global:
290
- print("❌ VADER or NewsFetcher not initialized! News analysis will be skipped.")
291
-
292
- print("\n🔍 Layer 1.1: Rapid Screening (data_manager V7.3)...")
293
- layer1_candidates = await data_manager_global.layer1_rapid_screening()
294
- if not layer1_candidates: print("❌ No candidates found in Layer 1.1"); return []
295
- print(f"✅ Selected {len(layer1_candidates)} symbols for Layer 1.2")
296
-
297
- print(f"\n📊 Layer 1.2: Fetching OHLCV data for {len(layer1_candidates)} symbols (Streaming)...")
298
- DATA_QUEUE_MAX_SIZE = 2
299
- ohlcv_data_queue = asyncio.Queue(maxsize=DATA_QUEUE_MAX_SIZE)
300
- ml_results_list = []
301
- market_context = await data_manager_global.get_market_context_async()
302
-
303
- ml_processor = MLProcessor(market_context, data_manager_global, learning_hub_global)
304
-
305
- batch_size = 15
306
- total_batches = (len(layer1_candidates) + batch_size - 1) // batch_size
307
-
308
- async def ml_consumer_task(queue: asyncio.Queue, results_list: list, whale_data_store: dict):
309
- # ... (لا تغيير في هذه الدالة الداخلية) ...
310
- batch_num = 0
311
- while True:
312
- try:
313
- batch_data = await queue.get()
314
- if batch_data is None:
315
- queue.task_done()
316
- break
317
- batch_num += 1
318
- batch_results_dict = await process_batch_parallel(
319
- batch_data, ml_processor, batch_num, total_batches, whale_data_store
320
- )
321
- results_list.append(batch_results_dict)
322
- queue.task_done()
323
- except Exception as e:
324
- print(f"❌ [ML Consumer] Fatal Error: {e}");
325
- traceback.print_exc();
326
- queue.task_done()
327
-
328
- consumer_task = asyncio.create_task(ml_consumer_task(ohlcv_data_queue, ml_results_list, preloaded_whale_data_dict))
329
 
330
- producer_task = asyncio.create_task(data_manager_global.stream_ohlcv_data(layer1_candidates, ohlcv_data_queue))
 
 
 
331
 
332
- await producer_task;
333
- await ohlcv_data_queue.join()
334
- await consumer_task;
335
-
336
- print("🔄 Aggregating all ML (Layer 1.3) results...")
337
- for batch_result in ml_results_list:
338
- for success_item in batch_result['success']:
339
- symbol = success_item['symbol']
340
- l1_data = success_item
341
-
342
- if l1_data:
343
- success_item['reasons_for_candidacy'] = l1_data.get('reasons_for_candidacy', [])
344
- success_item['layer1_score'] = l1_data.get('layer1_score', 0)
345
-
346
- success_item['whale_data'] = {'data_available': False, 'reason': 'Not fetched yet'}
347
- # (V8.2) إضافة قيم افتراضية للأخبار
348
- success_item['news_text'] = ""
349
- success_item['news_score_raw'] = 0.0 # درجة VADER الخام
350
- success_item['statistical_news_pnl'] = 0.0 # الدرجة المتعلمة
351
-
352
- # (V8.3) تمرير البيانات التي يحتاجها تحليل الحيتان
353
- original_l1_data = next((c for c in layer1_candidates if c['symbol'] == symbol), None)
354
- if original_l1_data:
355
- success_item['dollar_volume'] = original_l1_data.get('dollar_volume', 0.0)
356
-
357
- layer2_candidates.append(success_item)
358
-
359
- if not layer2_candidates: print("❌ No candidates found in Layer 1.3"); return []
360
-
361
- layer2_candidates.sort(key=lambda x: x.get('enhanced_final_score', 0), reverse=True)
362
-
363
- target_count = min(10, len(layer2_candidates))
364
-
365
- final_layer2_candidates = layer2_candidates[:target_count]
366
-
367
- print(f"\n🐋📰 Layer 1.4 (Optimized): Fetching Whale Data & News for top {len(final_layer2_candidates)} candidates...")
368
-
369
- # (دالة مساعدة لجلب الحيتان)
370
- async def get_whale_data_for_candidate(candidate):
371
- symbol = candidate.get('symbol', 'UNKNOWN')
372
- symbol_daily_volume = candidate.get('dollar_volume', 0.0)
373
 
374
- try:
375
- data = await data_manager_global.get_whale_data_for_symbol(
376
- symbol,
377
- daily_volume_usd=symbol_daily_volume
378
- )
379
- if data:
380
- candidate['whale_data'] = data
381
- else:
382
- candidate['whale_data'] = {'data_available': False, 'reason': 'No data returned'}
383
- except Exception as e:
384
- print(f" ❌ [Whale Fetch] {symbol} - Error: {e}")
385
- candidate['whale_data'] = {'data_available': False, 'error': str(e)}
386
-
387
- # (دالة مساعدة لجلب الأخبار وتحليل VADER)
388
- async def get_news_data_for_candidate(candidate):
389
- symbol = candidate.get('symbol', 'UNKNOWN')
390
- if not news_fetcher_global or not VADER_ANALYZER:
391
- candidate['news_text'] = "News analysis disabled."
392
- candidate['news_score_raw'] = 0.0
393
- return
394
-
395
- try:
396
- news_text = await news_fetcher_global.get_news_for_symbol(symbol)
397
- candidate['news_text'] = news_text
398
-
399
- if "No specific news found" in news_text or not news_text:
400
- candidate['news_score_raw'] = 0.0
401
- else:
402
- vader_score = VADER_ANALYZER.polarity_scores(news_text)
403
- candidate['news_score_raw'] = vader_score.get('compound', 0.0)
404
-
405
- except Exception as e:
406
- print(f" ❌ [News Fetch] {symbol} - Error: {e}")
407
- candidate['news_text'] = f"Error fetching news: {e}"
408
- candidate['news_score_raw'] = 0.0
409
-
410
- # (تنفيذ المهام بالتوازي)
411
- tasks = []
412
- for candidate in final_layer2_candidates:
413
- tasks.append(asyncio.create_task(get_whale_data_for_candidate(candidate)))
414
- tasks.append(asyncio.create_task(get_news_data_for_candidate(candidate)))
415
-
416
- await asyncio.gather(*tasks)
417
- print(" ✅ Whale data and News data fetched for top candidates.")
418
-
419
- print(" 🔄 Re-calculating enhanced scores with new Whale & Statistical News data...")
420
- for candidate in final_layer2_candidates:
421
- try:
422
- raw_vader_score = candidate.get('news_score_raw', 0.0)
423
-
424
- if learning_hub_global:
425
- statistical_pnl = await learning_hub_global.get_statistical_news_score(raw_vader_score)
426
- candidate['statistical_news_pnl'] = statistical_pnl
427
- else:
428
- candidate['statistical_news_pnl'] = 0.0
429
-
430
- new_score = ml_processor._calculate_enhanced_final_score(candidate)
431
- candidate['enhanced_final_score'] = new_score
432
- except Exception as e:
433
- print(f" ❌ [Score Recalc] {candidate.get('symbol')} - Error: {e}")
434
-
435
- final_layer2_candidates.sort(key=lambda x: x.get('enhanced_final_score', 0), reverse=True)
436
- print(" ✅ Top scores updated (with Stat. News + Whale) and re-sorted.")
437
-
438
-
439
- print(f"\n🔬 Layer 1.5: Running Advanced MC (GARCH+LGBM) on top {len(final_layer2_candidates)} candidates...")
440
- advanced_mc_analyzer = ml_processor.monte_carlo_analyzer
441
- updated_candidates_for_llm = []
442
- for candidate in final_layer2_candidates:
443
- symbol = candidate.get('symbol', 'UNKNOWN')
444
- try:
445
- advanced_mc_results = await advanced_mc_analyzer.generate_1h_distribution_advanced(
446
- candidate.get('ohlcv')
447
- )
448
- if advanced_mc_results and advanced_mc_results.get('simulation_model') == 'Phase2_GARCH_LGBM':
449
-
450
- # 🔴 --- START OF CHANGE (V8.7 - MC Expected Return Fix) --- 🔴
451
- # (إصلاح: حساب العائد المتوقع يدوياً إذا كان مفقوداً)
452
- if 'expected_return_pct' not in advanced_mc_results:
453
- try:
454
- mean_price = advanced_mc_results.get('distribution_summary', {}).get('mean_price', 0)
455
- current_price = advanced_mc_results.get('current_price', 0)
456
- if mean_price > 0 and current_price > 0:
457
- expected_return_pct = (mean_price - current_price) / current_price
458
- advanced_mc_results['expected_return_pct'] = expected_return_pct
459
- print(f" [MC Patch] {symbol}: Calculated Expected Return: {expected_return_pct:+.2%}")
460
- else:
461
- advanced_mc_results['expected_return_pct'] = 0.0
462
- except Exception:
463
- advanced_mc_results['expected_return_pct'] = 0.0
464
- # 🔴 --- END OF CHANGE --- 🔴
465
-
466
- candidate['monte_carlo_distribution'] = advanced_mc_results
467
- candidate['monte_carlo_probability'] = advanced_mc_results.get('probability_of_gain', 0)
468
- candidate['advanced_mc_run'] = True
469
- else:
470
- candidate['advanced_mc_run'] = False
471
- updated_candidates_for_llm.append(candidate)
472
- except Exception as e:
473
- print(f" ❌ [Advanced MC] {symbol} - Error: {e}. Using Phase 1 results.")
474
- candidate['advanced_mc_run'] = False
475
- updated_candidates_for_llm.append(candidate)
476
-
477
- print(" 🔄 Sanitizing final candidates for JSON serialization...")
478
- sanitized_candidates = []
479
- for cand in updated_candidates_for_llm:
480
- sanitized_candidates.append(_sanitize_results_for_json(cand))
481
-
482
- final_layer2_candidates = sanitized_candidates
483
-
484
- await r2_service_global.save_candidates_async(final_layer2_candidates)
485
-
486
- print("\n🧠 Layer 1.6: LLM Strategic Analysis (Explorer Brain)...")
487
- top_5_for_llm = final_layer2_candidates[:5]
488
- print(f" (Sending Top {len(top_5_for_llm)} candidates to LLM)")
489
-
490
- for candidate in top_5_for_llm:
491
- try:
492
- symbol = candidate['symbol']
493
- ohlcv_data = candidate.get('ohlcv');
494
- if not ohlcv_data: continue
495
-
496
- candidate['raw_ohlcv'] = ohlcv_data
497
- total_candles = sum(len(data) for data in ohlcv_data.values()) if ohlcv_data else 0
498
- if total_candles < 30: continue
499
-
500
- candidate['sentiment_data'] = await data_manager_global.get_market_context_async()
501
-
502
- llm_analysis = await llm_service_global.get_trading_decision(candidate)
503
-
504
- if llm_analysis and llm_analysis.get('action') in ['WATCH']:
505
- strategy_to_watch = llm_analysis.get('strategy_to_watch', 'GENERIC')
506
- confidence = llm_analysis.get('confidence_level', 0)
507
-
508
- watchlist_entry = {
509
- 'symbol': symbol,
510
- 'strategy_hint': strategy_to_watch,
511
- 'explorer_score': candidate.get('enhanced_final_score', 0),
512
- 'llm_confidence': confidence,
513
- 'analysis_timestamp': datetime.now().isoformat(),
514
- 'llm_decision_context': {
515
- 'decision': llm_analysis,
516
- 'full_candidate_data': candidate
517
- }
518
- }
519
- watchlist_candidates.append(watchlist_entry)
520
- print(f" ✅ {symbol}: Added to Sentry Watchlist (Strategy: {strategy_to_watch} | Conf: {confidence:.2f})")
521
- else:
522
- action = llm_analysis.get('action', 'NO_DECISION') if llm_analysis else 'NO_RESPONSE';
523
- print(f" ⚠️ {symbol}: Not recommended by LLM for watching ({action})")
524
- except Exception as e: print(f"❌ Error in LLM analysis for {candidate.get('symbol')}: {e}"); traceback.print_exc(); continue
525
-
526
- if watchlist_candidates:
527
- watchlist_candidates.sort(key=lambda x: (x['llm_confidence'] + x['explorer_score']) / 2, reverse=True)
528
-
529
- if not watchlist_candidates:
530
- print("❌ Explorer analysis complete: No suitable candidates for Sentry Watchlist.")
531
- return []
532
-
533
- top_watchlist = watchlist_candidates
534
-
535
- print("📊 إنشاء سجل تدقيق لمحرك الأنماط V8...")
536
- audit_log = {
537
- "log_id": f"audit_{int(datetime.now().timestamp())}",
538
- "timestamp": datetime.now().isoformat(),
539
- "model_key": "lgbm_pattern_model_combined.pkl",
540
- "scaler_key": "scaler_combined.pkl",
541
- "model_accuracy": 0.5870,
542
- "predictions": []
543
- }
544
-
545
- for candidate in final_layer2_candidates:
546
- pattern_analysis = candidate.get('pattern_analysis', {})
547
- audit_entry = {
548
- "symbol": candidate.get('symbol', 'N/A'),
549
- "timeframe": pattern_analysis.get('timeframe', 'N/A'),
550
- "pattern_detected": pattern_analysis.get('pattern_detected', 'N/A'),
551
- "confidence": pattern_analysis.get('pattern_confidence', 0),
552
- "predicted_direction": pattern_analysis.get('predicted_direction', 'neutral'),
553
- "error": "None"
554
- }
555
- audit_log["predictions"].append(audit_entry)
556
-
557
- if r2_service_global:
558
- await r2_service_global.save_analysis_audit_log_async(audit_log)
559
-
560
- print(f"✅ Explorer analysis complete. Sending {len(top_watchlist)} candidates to Sentry.")
561
- return top_watchlist
562
-
563
- except Exception as error:
564
- print(f"❌ Fatal error in Explorer (Layer 1) system: {error}"); traceback.print_exc()
565
- return []
566
-
567
-
568
- async def re_analyze_open_trade_async(trade_data):
569
- """(V8.7) إضافة إصلاح ndarray + إصلاح العائد المتوقع"""
570
- symbol = trade_data.get('symbol')
571
- try:
572
- async with state_manager.trade_analysis_lock:
573
- print(f"🔄 [Re-Analyze] Starting strategic analysis for {symbol}...")
574
- market_context = await data_manager_global.get_market_context_async()
575
- ohlcv_data_list = []
576
- temp_queue = asyncio.Queue()
577
 
578
- await data_manager_global.stream_ohlcv_data(
579
- [{'symbol': symbol, 'layer1_score': 0, 'reasons_for_candidacy': ['re-analysis']}],
580
- temp_queue
581
- )
582
 
583
- while True:
584
- try:
585
- batch = await asyncio.wait_for(temp_queue.get(), timeout=1.0)
586
- if batch is None: temp_queue.task_done(); break
587
- ohlcv_data_list.extend(batch)
588
- temp_queue.task_done()
589
- except asyncio.TimeoutError:
590
- if temp_queue.empty(): break
591
- except Exception: break
592
-
593
- if not ohlcv_data_list: print(f"⚠️ Failed to get re-analysis data for {symbol}"); return None
594
- ohlcv_data = ohlcv_data_list[0]
595
-
596
- print(f" 🔄 [Re-Analyze] Fetching current daily volume for {symbol}...")
597
- symbol_daily_volume = await data_manager_global.get_symbol_daily_volume(symbol)
598
 
599
- re_analysis_whale_data = await data_manager_global.get_whale_data_for_symbol(
600
- symbol,
601
- daily_volume_usd=symbol_daily_volume
602
- )
603
-
604
- ml_processor = MLProcessor(market_context, data_manager_global, learning_hub_global)
605
-
606
- print(f"🔄 [Re-Analyze] Using Advanced MC (Phase 2+3) for {symbol}...")
607
- advanced_mc_results = await ml_processor.monte_carlo_analyzer.generate_1h_distribution_advanced(
608
- ohlcv_data.get('ohlcv')
609
- )
610
-
611
- processed_data = await ml_processor.process_and_score_symbol_enhanced(ohlcv_data, {symbol: re_analysis_whale_data} if re_analysis_whale_data else {})
612
- if not processed_data: return None
613
-
614
- if advanced_mc_results:
615
-
616
- # 🔴 --- START OF CHANGE (V8.7 - MC Expected Return Fix) --- 🔴
617
- # (إصلاح: حساب العائد المتوقع يدوياً إذا كان مفقوداً)
618
- if 'expected_return_pct' not in advanced_mc_results:
619
- try:
620
- mean_price = advanced_mc_results.get('distribution_summary', {}).get('mean_price', 0)
621
- current_price = advanced_mc_results.get('current_price', 0)
622
- if mean_price > 0 and current_price > 0:
623
- expected_return_pct = (mean_price - current_price) / current_price
624
- advanced_mc_results['expected_return_pct'] = expected_return_pct
625
- print(f" [MC Patch] {symbol}: Calculated Expected Return: {expected_return_pct:+.2%}")
626
- else:
627
- advanced_mc_results['expected_return_pct'] = 0.0
628
- except Exception:
629
- advanced_mc_results['expected_return_pct'] = 0.0
630
- # 🔴 --- END OF CHANGE --- 🔴
631
-
632
- processed_data['monte_carlo_distribution'] = advanced_mc_results
633
- processed_data['monte_carlo_probability'] = advanced_mc_results.get('probability_of_gain', 0)
634
-
635
- processed_data['raw_ohlcv'] = ohlcv_data.get('raw_ohlcv') or ohlcv_data.get('ohlcv')
636
- processed_data['ohlcv'] = processed_data['raw_ohlcv']
637
- processed_data['sentiment_data'] = market_context
638
 
639
- if news_fetcher_global and VADER_ANALYZER:
640
- try:
641
- news_text = await news_fetcher_global.get_news_for_symbol(symbol)
642
- processed_data['news_text'] = news_text
643
-
644
- vader_score = VADER_ANALYZER.polarity_scores(news_text)
645
- processed_data['news_score'] = vader_score.get('compound', 0.0)
646
- except Exception as e:
647
- print(f" ❌ [Re-Analyze News] {symbol} - Error: {e}")
648
- processed_data['news_text'] = "News analysis failed."
649
- processed_data['news_score'] = 0.0
650
  else:
651
- processed_data['news_text'] = "News analysis disabled."
652
- processed_data['news_score'] = 0.0
653
-
654
- # 🔴 --- START OF CHANGE (V8.6 - ndarray Fix) --- 🔴
655
- # (يجب تنقية البيانات قبل إرسالها إلى LLMService لتجنب خطأ ndarray)
656
- print(f" 🔄 [Re-Analyze] Sanitizing data for {symbol} before LLM log...")
657
- sanitized_processed_data = _sanitize_results_for_json(processed_data)
658
-
659
- re_analysis_decision = await llm_service_global.re_analyze_trade_async(trade_data, sanitized_processed_data)
660
- # 🔴 --- END OF CHANGE --- 🔴
661
-
662
- if re_analysis_decision:
663
- await r2_service_global.save_system_logs_async({ "trade_reanalyzed": True, "symbol": symbol, "action": re_analysis_decision.get('action'), 'strategy': re_analysis_decision.get('strategy', 'GENERIC') })
664
- print(f"✅ [Re-Analyze] Strategic analysis complete for {symbol}. Decision: {re_analysis_decision.get('action')}")
665
- return {"symbol": symbol, "decision": re_analysis_decision, "current_price": processed_data.get('current_price')}
666
- else: return None
667
- except Exception as error: await r2_service_global.save_system_logs_async({ "reanalysis_error": True, "symbol": symbol, "error": str(error) }); print(f"❌ Error in re_analyze_open_trade_async for {symbol}: {error}"); traceback.print_exc(); return None
668
 
 
 
 
669
 
670
  async def run_bot_cycle_async():
671
- # ... (لا تغيير في هذه الدالة) ...
672
- """
673
- (محدث V5.9) - دورة البوت الرئيسية (المستكشف)
674
- """
675
- try:
676
- if not await state_manager.wait_for_initialization():
677
- print("❌ Services not fully initialized - skipping cycle"); return
678
-
679
- await asyncio.sleep(1.0)
680
-
681
- print("🔄 Starting Explorer cycle (Layer 1)...");
682
- await r2_service_global.save_system_logs_async({"explorer_cycle_started": True})
683
-
684
- if not r2_service_global.acquire_lock():
685
- print("❌ Failed to acquire lock - skipping cycle (another cycle likely running)"); return
686
-
687
- open_trades = []
688
- try:
689
- open_trades = await trade_manager_global.get_open_trades();
690
- print(f"📋 Open trades: {len(open_trades)}")
691
-
692
- if open_trades:
693
- now = datetime.now()
694
- trades_to_reanalyze = [t for t in open_trades if now >= datetime.fromisoformat(t.get('expected_target_time', now.isoformat()))]
695
-
696
- if trades_to_reanalyze:
697
- print(f"🔄 (Explorer) Re-analyzing {len(trades_to_reanalyze)} trades strategically...")
698
- reanalysis_results = await asyncio.gather(*[re_analyze_open_trade_async(trade) for trade in trades_to_reanalyze], return_exceptions=True)
699
-
700
- for i, result in enumerate(reanalysis_results):
701
- trade = trades_to_reanalyze[i]
702
- if isinstance(result, Exception):
703
- print(f" ❌ Re-analysis failed for {trade.get('symbol')}: {result}")
704
-
705
- elif result and result['decision'].get('action') == "UPDATE_TRADE":
706
- print(f" ✅ (Explorer) Updating strategy for {trade.get('symbol')}.");
707
- await trade_manager_global.update_trade_strategy(trade, result['decision'])
708
-
709
- elif result and result['decision'].get('action') == "HOLD":
710
- print(f" ℹ️ (Explorer) Holding {trade.get('symbol')}. Resetting 15-min timer.")
711
- await trade_manager_global.update_trade_strategy(trade, result['decision'])
712
-
713
- elif result and result['decision'].get('action') == "CLOSE_TRADE":
714
- print(f" 🛑 (Explorer) LLM Re-analysis ordered CLOSE_TRADE for {trade.get('symbol')}. Executing...")
715
- await trade_manager_global.immediate_close_trade(
716
- trade.get('symbol'),
717
- result['current_price'],
718
- f"Strategic Exit: LLM Re-analysis ({result['decision'].get('reasoning', 'N/A')[:50]}...)"
719
- )
720
-
721
- elif result:
722
- print(f" ℹ️ (Explorer) Re-analysis returned unhandled action '{result['decision'].get('action')}' for {trade.get('symbol')}.")
723
-
724
- else:
725
- print(f" ⚠️ Re-analysis for {trade.get('symbol')} yielded no decision.")
726
-
727
- current_open_trades_count = len(await trade_manager_global.get_open_trades())
728
- should_look_for_new_trade = current_open_trades_count == 0
729
-
730
- if should_look_for_new_trade:
731
- portfolio_state = await r2_service_global.get_portfolio_state_async();
732
- current_capital = portfolio_state.get("current_capital_usd", 0)
733
- if current_capital > 1:
734
- print("🎯 (Explorer) Looking for new trading opportunities...")
735
-
736
- sentry_watchlist = await run_3_layer_analysis_explorer()
737
-
738
- if sentry_watchlist:
739
- print(f"✅ (Explorer) Found {len(sentry_watchlist)} candidates. Sending to Sentry (Layer 2)...")
740
- await trade_manager_global.update_sentry_watchlist(sentry_watchlist)
741
- else:
742
- print("❌ (Explorer) No suitable trading opportunities found for Sentry.")
743
- await trade_manager_global.update_sentry_watchlist([])
744
- else:
745
- print("❌ Insufficient capital to open new trades")
746
- else:
747
- print("ℹ️ A trade is already open, skipping new trade search.")
748
- await trade_manager_global.update_sentry_watchlist([])
749
 
750
- finally:
751
- if r2_service_global.lock_acquired: r2_service_global.release_lock()
752
- await r2_service_global.save_system_logs_async({ "explorer_cycle_completed": True, "open_trades": len(open_trades)})
753
- print("✅ Explorer cycle complete")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754
 
755
- except Exception as error:
756
- print(f"❌ Unhandled error in main cycle: {error}"); traceback.print_exc()
757
- await r2_service_global.save_system_logs_async({ "cycle_error": True, "error": str(error) });
758
- if r2_service_global and r2_service_global.lock_acquired: r2_service_global.release_lock()
759
 
760
  @asynccontextmanager
761
  async def lifespan(application: FastAPI):
762
- # (V8.4 - Whale Learning Loop)
763
- """Application lifecycle management"""
764
- print("🚀 Starting application initialization (Explorer/Sentry/Executor)...")
765
- try:
766
- success = await initialize_services()
767
- if not success: print("❌ Application initialization failed - shutting down..."); yield; return
768
-
769
- # (تشغيل المهام الخلفية)
770
  asyncio.create_task(monitor_market_async())
771
  asyncio.create_task(trade_manager_global.start_sentry_and_monitoring_loops())
772
- asyncio.create_task(run_periodic_distillation())
773
-
774
- # (جديد: تشغيل حلقة تعلم الحيتان التي أضفناها إلى hub_manager)
775
- if learning_hub_global and hasattr(learning_hub_global, 'run_whale_learning_check'):
776
- asyncio.create_task(learning_hub_global.run_whale_learning_check())
777
- print(" -> 🐋 Whale Learning Loop (V3) is scheduled")
778
- else:
779
- print(" -> ⚠️ Whale Learning Loop is NOT scheduled (function not found)")
780
-
781
- await r2_service_global.save_system_logs_async({"application_started": True})
782
- print("🎯 Application ready - Explorer-Sentry-Executor Architecture is active")
783
- print(" -> 📈 Sentry (Layer 2) & Executor (Layer 3) are active")
784
- print(" -> 🧠 Periodic Distillation (Curator) is scheduled")
785
  yield
786
- except Exception as error:
787
- print(f"❌ Application startup failed: {error}");
788
- traceback.print_exc()
789
- if r2_service_global:
790
- await r2_service_global.save_system_logs_async({ "application_startup_failed": True, "error": str(error) })
791
- raise
792
- finally:
793
- await cleanup_on_shutdown()
794
-
795
-
796
- application = FastAPI(lifespan=lifespan, title="AI Trading Bot", description="Explorer-Sentry-Executor Architecture (V5.9)", version="5.9.0")
797
-
798
- @application.get("/")
799
- # ... (لا تغيير في نقاط النهاية (Endpoints)) ...
800
- async def root(): return {"message": "Welcome to the AI Trading System", "system": "Explorer-Sentry-Executor", "status": "running" if state_manager.initialization_complete else "initializing", "timestamp": datetime.now().isoformat()}
801
-
802
- @application.get("/run-cycle")
803
- async def run_cycle_api():
804
- if not state_manager.initialization_complete: raise HTTPException(status_code=503, detail="Services not fully initialized")
805
- asyncio.create_task(run_bot_cycle_async())
806
- return {"message": "Explorer (Layer 1) cycle initiated", "system": "Explorer-Sentry-Executor"}
807
-
808
- @application.get("/health")
809
- async def health_check(): return {"status": "healthy" if state_manager.initialization_complete else "initializing", "initialization_complete": state_manager.initialization_complete, "services_initialized": state_manager.services_initialized, "initialization_error": state_manager.initialization_error, "timestamp": datetime.now().isoformat(), "system_architecture": "Explorer-Sentry-Executor (V5.9)"}
810
-
811
- @application.get("/analyze-market")
812
- async def analyze_market_api():
813
- if not state_manager.initialization_complete: raise HTTPException(status_code=503, detail="Services not fully initialized")
814
- result = await run_3_layer_analysis_explorer()
815
- if result: return {"watchlist_generated": True, "count": len(result), "top_candidate": result[0]}
816
- else: return {"watchlist_generated": False, "message": "No suitable candidates found for Sentry"}
817
-
818
- @application.get("/portfolio")
819
- async def get_portfolio_api():
820
- if not state_manager.initialization_complete: raise HTTPException(status_code=503, detail="Services not fully initialized")
821
- try: portfolio_state = await r2_service_global.get_portfolio_state_async(); open_trades = await trade_manager_global.get_open_trades(); return {"portfolio": portfolio_state, "open_trades": open_trades, "timestamp": datetime.now().isoformat()}
822
- except Exception as e: raise HTTPException(status_code=500, detail=f"Error getting portfolio: {str(e)}")
823
-
824
- @application.get("/system-status")
825
- async def get_system_status():
826
- monitoring_status = trade_manager_global.get_sentry_status() if trade_manager_global else {};
827
 
828
- return {"initialization_complete": state_manager.initialization_complete, "services_initialized": state_manager.services_initialized, "initialization_error": state_manager.initialization_error, "market_state_ok": MARKET_STATE_OK, "sentry_status": monitoring_status, "timestamp": datetime.now().isoformat()}
829
-
830
- async def cleanup_on_shutdown():
831
- # ... (لا تغيير في هذه الدالة) ...
832
- global r2_service_global, data_manager_global, trade_manager_global, learning_hub_global, symbol_whale_monitor_global
833
-
834
- print("🛑 Shutdown signal received. Cleaning up...")
835
-
836
- if trade_manager_global:
837
- await trade_manager_global.stop_sentry_loops()
838
- print("✅ Sentry/Executor loops stopped")
839
-
840
- if learning_hub_global and learning_hub_global.initialized:
841
- try:
842
- await learning_hub_global.shutdown()
843
- print("✅ Learning hub data saved")
844
- except Exception as e: print(f"❌ Failed to save learning hub data: {e}")
845
-
846
- if symbol_whale_monitor_global:
847
- try:
848
- await symbol_whale_monitor_global.cleanup()
849
- print("✅ Whale monitor cleanup complete.")
850
- except Exception as e:
851
- print(f"❌ Failed to cleanup whale monitor: {e}")
852
-
853
- if data_manager_global: await data_manager_global.close(); print("✅ Data manager closed")
854
- if r2_service_global:
855
- try: await r2_service_global.save_system_logs_async({"application_shutdown": True}); print("✅ Shutdown log saved")
856
- except Exception as e: print(f"❌ Failed to save shutdown log: {e}")
857
- if r2_service_global.lock_acquired: r2_service_global.release_lock(); print("✅ R2 lock released")
858
-
859
- def signal_handler(signum, frame): print(f"🛑 Received signal {signum}. Initiating shutdown..."); asyncio.create_task(cleanup_on_shutdown()); sys.exit(0)
860
- signal.signal(signal.SIGINT, signal_handler); signal.signal(signal.SIGTERM, signal_handler)
 
861
 
862
  if __name__ == "__main__":
863
- print("🚀 Starting AI Trading Bot (Explorer-Sentry-Executor V5.9)...")
864
- uvicorn.run( application, host="0.0.0.0", port=7860, log_level="info", access_log=True )
 
1
+ # app.py (V11.0 - Full Real Data Integration & New Engines)
2
  import os
3
  import traceback
4
  import signal
 
10
  from contextlib import asynccontextmanager
11
  from fastapi import FastAPI, HTTPException
12
  from datetime import datetime
13
+ from typing import List, Dict, Any
14
 
15
+ # استيراد الخدمات الأساسية
16
  try:
17
  from r2 import R2Service
18
  from LLM import LLMService
19
+ from ml_engine.data_manager import DataManager
20
  from ml_engine.processor import MLProcessor
21
  from learning_hub.hub_manager import LearningHubManager
 
22
  from trade_manager import TradeManager
23
+ # لم نعد بحاجة لاستيراد WhaleMonitor أو NewsFetcher هنا مباشرة،
24
+ # سيتم التعامل معها داخل DataManager أو Processor عند الحاجة.
 
25
  except ImportError as e:
26
+ print(f"❌ [App] خطأ فادح في استيراد الوحدات: {e}")
 
 
27
  sys.exit(1)
28
 
29
+ # المتغيرات العالمية للنظام
 
 
 
 
 
 
 
 
 
 
30
  r2_service_global = None
31
  data_manager_global = None
32
  llm_service_global = None
33
  learning_hub_global = None
34
  trade_manager_global = None
35
+ ml_processor_global = None # (V11.0) إضافة المعالج كمتغير عالمي
 
 
36
 
37
  MARKET_STATE_OK = True
38
 
 
39
  class StateManager:
 
40
  def __init__(self):
41
  self.market_analysis_lock = asyncio.Lock()
42
  self.trade_analysis_lock = asyncio.Lock()
43
  self.initialization_complete = False
44
  self.initialization_error = None
45
  self.services_initialized = {
46
+ 'r2': False, 'data': False, 'llm': False, 'hub': False, 'trade': False
 
 
47
  }
48
 
49
  async def wait_for_initialization(self, timeout=60):
 
58
  self.services_initialized[service_name] = True
59
  if all(self.services_initialized.values()):
60
  self.initialization_complete = True
61
+ print("🎯 [System] جميع الخدمات مهيأة وجاهزة للعمل.")
62
 
63
  def set_initialization_error(self, error):
64
  self.initialization_error = error
 
67
  state_manager = StateManager()
68
 
69
  async def initialize_services():
70
+ """تهيئة مركزية لجميع خدمات النظام بترتيب محدد"""
71
  global r2_service_global, data_manager_global, llm_service_global
72
+ global learning_hub_global, trade_manager_global, ml_processor_global
73
+
74
  try:
75
+ print("🚀 [System V11.0] بدء تهيئة الخدمات...")
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ # 1. الطبقة الأساسية (R2 & Data)
78
+ r2_service_global = R2Service()
79
+ state_manager.set_service_initialized('r2')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
+ # تحميل قاعدة بيانات العقود (اختياري، يمكن أن يكون فارغاً إذا لم يستخدم)
82
+ contracts_db = await r2_service_global.load_contracts_db_async() or {}
 
 
 
83
 
84
+ # تهيئة DataManager بدون WhaleMonitor مؤقتاً (سيتم ربطه لاحقاً إذا لزم الأمر)
85
+ data_manager_global = DataManager(contracts_db, whale_monitor=None, r2_service=r2_service_global)
86
+ await data_manager_global.initialize()
87
+ state_manager.set_service_initialized('data')
88
+
89
+ # 2. طبقة الذكاء (LLM & Learning Hub)
90
+ llm_service_global = LLMService()
91
+ llm_service_global.r2_service = r2_service_global
92
+
93
+ learning_hub_global = LearningHubManager(r2_service_global, llm_service_global, data_manager_global)
 
 
 
94
  await learning_hub_global.initialize()
95
+ llm_service_global.learning_hub = learning_hub_global # ربط عكسي
96
+ state_manager.set_service_initialized('llm')
97
+ state_manager.set_service_initialized('hub')
98
+
99
+ # 3. طبقة التنفيذ والمعالجة (Trade Manager & Processor)
100
+ # تهيئة المعالج المركزي الذي يربط كل شيء
101
+ ml_processor_global = MLProcessor(
102
+ market_context=None, # سيتم تحديثه في كل دورة
103
+ data_manager=data_manager_global,
104
+ learning_hub=learning_hub_global
105
+ )
106
 
 
 
 
107
  trade_manager_global = TradeManager(
108
+ r2_service_global, learning_hub_global, data_manager_global,
109
+ state_manager, callback_on_close=run_bot_cycle_async
 
 
 
110
  )
 
111
  await trade_manager_global.initialize_sentry_exchanges()
112
+ state_manager.set_service_initialized('trade')
 
113
 
114
+ return True
115
+ except Exception as e:
116
+ error_msg = f"فشل تهيئة الخدمات: {str(e)}"
117
+ print(f"❌ {error_msg}")
118
+ state_manager.set_initialization_error(error_msg)
119
+ return False
120
 
121
  async def monitor_market_async():
122
+ global MARKET_STATE_OK
 
123
  try:
124
+ if not await state_manager.wait_for_initialization(): return
125
  while True:
126
  try:
127
  async with state_manager.market_analysis_lock:
128
+ market_ctx = await data_manager_global.get_market_context_async()
129
+ ml_processor_global.market_context = market_ctx # تحديث سياق المعالج
130
 
131
+ # فحص بسيط لحالة السوق (يمكن تعقيده لاحقاً)
132
+ if market_ctx.get('btc_sentiment') == 'BEARISH' and market_ctx.get('fear_and_greed_index', 50) < 25:
133
+ if MARKET_STATE_OK:
134
+ print("⚠️ [Market Monitor] ظروف سوق هابطة شديدة. إيقاف مؤقت.")
135
+ MARKET_STATE_OK = False
 
 
 
 
 
136
  else:
137
+ if not MARKET_STATE_OK:
138
+ print("✅ [Market Monitor] تحسن ظروف السوق. استئناف العمل.")
139
+ MARKET_STATE_OK = True
140
+
141
+ await asyncio.sleep(300) # فحص كل 5 دقائق
142
+ except Exception as e:
143
+ print(f"❌ [Market Monitor] خطأ: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
144
  await asyncio.sleep(60)
145
+ except Exception: pass
146
 
147
+ async def run_explorer_cycle():
148
+ """دورة المستكشف الرئيسية (Layer 1 Complete Cycle)"""
149
+ if not state_manager.initialization_complete:
150
+ print("❌ [Explorer] الخدمات غير جاهزة.")
151
+ return
152
 
153
+ print("🔭 [Explorer] بدء دورة استكشاف جديدة...")
 
 
 
 
 
 
154
  try:
155
+ if not MARKET_STATE_OK:
156
+ print("⏸️ [Explorer] السوق في حالة توقف. تخطي الدورة.")
157
+ return
158
+
159
+ # 1. الغربلة الأولية السريعة (Layer 1.1)
160
+ # تستخدم الآن (Ranker + XGB 1h + MC) داخلياً
161
+ candidates_l1 = await data_manager_global.layer1_rapid_screening()
162
+ if not candidates_l1:
163
+ print("😴 [Explorer] لم يتم العثور على مرشحين في الغربلة الأولية.")
164
+ # تحديث الحارس بقائمة فارغة إذا لم نجد شيئاً
165
+ await trade_manager_global.update_sentry_watchlist([])
166
+ return
167
+
168
+ # 2. التحليل العميق المتوازي (Layer 1.2 & 1.3)
169
+ print(f"🔬 [Explorer] تحليل عميق لـ {len(candidates_l1)} عملة...")
170
+ analyzed_candidates = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
+ # نستخدم Queue لتدفق البيانات بين DataManager و Processor
173
+ data_queue = asyncio.Queue(maxsize=5)
174
+ # بدء منتج البيانات في الخلفية
175
+ producer_task = asyncio.create_task(data_manager_global.stream_ohlcv_data(candidates_l1, data_queue))
176
 
177
+ while True:
178
+ # انتظار دفعة من البيانات
179
+ batch = await data_queue.get()
180
+ if batch is None: # إشارة نهاية التدفق
181
+ data_queue.task_done()
182
+ break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
+ # معالجة الدفعة بالتوازي باستخدام Processor المحدث
185
+ tasks = [ml_processor_global.process_and_score_symbol_enhanced(c) for c in batch]
186
+ results = await asyncio.gather(*tasks, return_exceptions=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
 
188
+ for res in results:
189
+ # قبول العملات التي تجاوزت درجة 60% فقط
190
+ if res and isinstance(res, dict) and res.get('enhanced_final_score', 0) >= 0.60:
191
+ analyzed_candidates.append(res)
192
 
193
+ data_queue.task_done()
194
+
195
+ # التأكد من انتهاء مهمة المنتج
196
+ await producer_task
197
+
198
+ # 3. الترشيح النهائي والاختيار (Layer 1.4 LLM)
199
+ if analyzed_candidates:
200
+ # ترتيب حسب الدرجة النهائية الجديدة
201
+ analyzed_candidates.sort(key=lambda x: x['enhanced_final_score'], reverse=True)
202
+ top_5 = analyzed_candidates[:5]
 
 
 
 
 
203
 
204
+ print(f"🧠 [Explorer] إرسال أفضل {len(top_5)} عملة إلى LLM للقرار النهائي...")
205
+ watchlist = []
206
+ for cand in top_5:
207
+ # استشارة النموذج اللغوي لاتخاذ قرار استراتيجي
208
+ decision = await llm_service_global.get_trading_decision(cand)
209
+ if decision and decision.get('action') == 'WATCH':
210
+ watchlist.append({
211
+ 'symbol': cand['symbol'],
212
+ 'strategy_hint': decision.get('strategy_to_watch', 'GENERIC'),
213
+ # نحتفظ بالسياق الكامل للقرار لاستخدامه لاحقاً في الحارس
214
+ 'llm_decision_context': {'decision': decision}
215
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
+ # تحديث قائمة مراقبة الحارس بالنتائج النهائية
218
+ if watchlist:
219
+ print(f"✅ [Explorer] تم اختيار {len(watchlist)} عملة للمراقبة.")
220
+ await trade_manager_global.update_sentry_watchlist(watchlist)
 
 
 
 
 
 
 
221
  else:
222
+ print("⚠️ [Explorer] رفض LLM جميع المرشحين.")
223
+ await trade_manager_global.update_sentry_watchlist([])
224
+ else:
225
+ print("📉 [Explorer] لم تنجح أي عملة في التحليل العميق (Score < 60%).")
226
+ await trade_manager_global.update_sentry_watchlist([])
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
+ except Exception as e:
229
+ print(f"❌ [Explorer Error] {e}")
230
+ traceback.print_exc()
231
 
232
  async def run_bot_cycle_async():
233
+ """دورة البوت الرئيسية (تستدعى دورياً أو عند الطلب)"""
234
+ if not state_manager.initialization_complete: return
235
+
236
+ # استخدام قفل R2 لمنع تداخل الدورات
237
+ if not r2_service_global.acquire_lock():
238
+ print("⚠️ [System] دورة أخرى قيد التشغيل. تخطي.")
239
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
+ try:
242
+ # 1. إدارة الصفقات المفتوحة (إعادة التحليل إذا لزم الأمر)
243
+ open_trades = await trade_manager_global.get_open_trades()
244
+ if open_trades:
245
+ now = datetime.now()
246
+ # إعادة تحليل الصفقات التي تجاوزت وقت هدفها المتوقع
247
+ trades_to_reanalyze = [t for t in open_trades if now >= datetime.fromisoformat(t.get('expected_target_time', now.isoformat()))]
248
+ for trade in trades_to_reanalyze:
249
+ print(f"🔄 [Re-Analyze] إعادة تحليل {trade['symbol']}...")
250
+ # (يمكن إضافة منطق إعادة التحليل هنا باستخدام LLM و Processor)
251
+ # حالياً سنقوم فقط بتمديد الوقت لتجنب التكرار المستمر
252
+ trade['expected_target_time'] = (now + timedelta(minutes=15)).isoformat()
253
+ await trade_manager_global._update_trade_in_db(trade)
254
+
255
+ # 2. تشغيل دورة المستكشف للبحث عن فرص جديدة
256
+ # (نبحث فقط إذا كان لدينا رأس مال متاح أو عدد صفقات قليل)
257
+ if len(open_trades) < 3:
258
+ await run_explorer_cycle()
259
+ else:
260
+ print("ℹ️ [System] الحد الأقصى للصفقات مفتوح. تخطي الاستكشاف.")
261
 
262
+ finally:
263
+ if r2_service_global.lock_acquired:
264
+ r2_service_global.release_lock()
 
265
 
266
  @asynccontextmanager
267
  async def lifespan(application: FastAPI):
268
+ """إدارة دورة حياة التطبيق"""
269
+ print("🏁 [System] بدء تشغيل التطبيق...")
270
+ success = await initialize_services()
271
+ if success:
272
+ # تشغيل المهام الخلفية
 
 
 
273
  asyncio.create_task(monitor_market_async())
274
  asyncio.create_task(trade_manager_global.start_sentry_and_monitoring_loops())
275
+ # تشغيل دورة استكشاف أولية بعد وقت قصير
276
+ asyncio.get_event_loop().call_later(10, lambda: asyncio.create_task(run_bot_cycle_async()))
277
+ yield
278
+ else:
279
+ print("❌ [System] فشل بدء التشغيل. الخروج...")
 
 
 
 
 
 
 
 
280
  yield
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
 
282
+ # تنظيف عند الإغلاق
283
+ print("🛑 [System] إيقاف التشغيل...")
284
+ if trade_manager_global: await trade_manager_global.stop_sentry_loops()
285
+ if data_manager_global: await data_manager_global.close()
286
+ if learning_hub_global: await learning_hub_global.shutdown()
287
+
288
+ app = FastAPI(lifespan=lifespan, title="AI Trading Bot V11.0")
289
+
290
+ @app.get("/")
291
+ async def root():
292
+ return {
293
+ "status": "running" if state_manager.initialization_complete else "initializing",
294
+ "system": "V11.0 (Real Data + XGBoost Integrated)",
295
+ "timestamp": datetime.now().isoformat()
296
+ }
297
+
298
+ @app.get("/force-cycle")
299
+ async def force_cycle():
300
+ """نقطة نهاية لتشغيل دورة استكشاف يدوياً"""
301
+ if not state_manager.initialization_complete:
302
+ raise HTTPException(status_code=503, detail="System not ready")
303
+ asyncio.create_task(run_bot_cycle_async())
304
+ return {"message": "Explorer cycle initiated manually"}
305
+
306
+ @app.get("/status")
307
+ async def system_status():
308
+ """عرض حالة النظام والحارس"""
309
+ sentry_status = trade_manager_global.get_sentry_status() if trade_manager_global else {}
310
+ return {
311
+ "initialization": state_manager.initialization_complete,
312
+ "market_ok": MARKET_STATE_OK,
313
+ "sentry": sentry_status,
314
+ "timestamp": datetime.now().isoformat()
315
+ }
316
 
317
  if __name__ == "__main__":
318
+ uvicorn.run(app, host="0.0.0.0", port=8000)