Riy777 commited on
Commit
99679e1
·
verified ·
1 Parent(s): 2d4b98a

Update data_manager.py

Browse files
Files changed (1) hide show
  1. data_manager.py +70 -64
data_manager.py CHANGED
@@ -1,4 +1,4 @@
1
- # data_manager.py (Updated to V7.1 - 1H Detector Screening)
2
  import os
3
  import asyncio
4
  import httpx
@@ -11,19 +11,15 @@ import logging
11
  from typing import List, Dict, Any
12
  import pandas as pd
13
 
14
- # 🔴 --- START OF CHANGE (V7.1) --- 🔴
15
- # (استيراد الأدوات اللازمة لـ "الكاشف المصغر")
16
  try:
17
  import pandas_ta as ta
18
  except ImportError:
19
  print("⚠️ مكتبة pandas_ta غير موجودة، فلتر الغربلة المتقدم سيفشل.")
20
  ta = None
21
 
22
- # (استيراد الوحدات الأساسية من محرك ML لتشغيل الكاشف المصغر)
23
  from ml_engine.indicators import AdvancedTechnicalAnalyzer
24
  from ml_engine.monte_carlo import MonteCarloAnalyzer
25
  from ml_engine.patterns import ChartPatternAnalyzer
26
- # 🔴 --- END OF CHANGE --- 🔴
27
 
28
  logging.getLogger("httpx").setLevel(logging.WARNING)
29
  logging.getLogger("httpcore").setLevel(logging.WARNING)
@@ -49,17 +45,14 @@ class DataManager:
49
  self.market_cache = {}
50
  self.last_market_load = None
51
 
52
- # 🔴 --- START OF CHANGE (V7.1) --- 🔴
53
- # (تهيئة أدوات الكاشف المصغر)
54
  self.technical_analyzer = AdvancedTechnicalAnalyzer()
55
  self.monte_carlo_analyzer = MonteCarloAnalyzer()
56
  self.pattern_analyzer = ChartPatternAnalyzer()
57
- # 🔴 --- END OF CHANGE --- 🔴
58
 
59
  async def initialize(self):
60
  self.http_client = httpx.AsyncClient(timeout=30.0)
61
  await self._load_markets()
62
- print("✅ DataManager initialized - V7.1 (1H Detector Screening)")
63
 
64
  async def _load_markets(self):
65
  try:
@@ -205,10 +198,10 @@ class DataManager:
205
  'data_quality': 'LOW'
206
  }
207
 
208
- # 🔴 --- START OF REFACTOR (V7.1 - 1H Detector Screening) --- 🔴
209
 
210
  def _create_dataframe(self, candles: List) -> pd.DataFrame:
211
- """(جديد V7.1) دالة مساعدة لإنشاء DataFrame لتحليل 1H"""
212
  try:
213
  if not candles: return pd.DataFrame()
214
  df = pd.DataFrame(candles, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
@@ -223,11 +216,22 @@ class DataManager:
223
 
224
  def _calculate_1h_filter_score(self, analysis: Dict) -> float:
225
  """
226
- (جديد V7.1) - "الكاشف المصغر"
227
- مقتبس من processor.py لحساب درجة (0-1) بناءً على بيانات 1H فقط.
228
- يستخدم (المؤشرات، مونت كارلو، الأنماط) فقط.
229
  """
230
  try:
 
 
 
 
 
 
 
 
 
 
 
 
231
  # 1. درجة الأنماط (Pattern Score)
232
  pattern_confidence = analysis.get('pattern_analysis', {}).get('pattern_confidence', 0)
233
 
@@ -235,7 +239,7 @@ class DataManager:
235
  mc_distribution = analysis.get('monte_carlo_distribution')
236
  monte_carlo_score = 0
237
 
238
- if mc_distribution:
239
  prob_gain = mc_distribution.get('probability_of_gain', 0)
240
  var_95_value = mc_distribution.get('risk_metrics', {}).get('VaR_95_value', 0)
241
  current_price = analysis.get('current_price', 1)
@@ -246,12 +250,10 @@ class DataManager:
246
  if normalized_var > 0.05: risk_penalty = 0.5
247
  elif normalized_var > 0.03: risk_penalty = 0.8
248
 
249
- # (تطبيع الاحتمالية: 0.5 -> 0, 1.0 -> 1.0)
250
  normalized_prob_score = max(0.0, (prob_gain - 0.5) * 2)
251
  monte_carlo_score = normalized_prob_score * risk_penalty
252
 
253
  # 3. درجة المؤشرات (Indicator Score)
254
- # (تقييم بسيط للمؤشرات الرئيسية لـ 1H)
255
  indicator_score = 0
256
  indicators = analysis.get('advanced_indicators', {}).get('1h', {})
257
  if indicators:
@@ -268,7 +270,6 @@ class DataManager:
268
  indicator_score = min(0.4 + (35 - rsi) / 35, 0.8)
269
 
270
  # 4. حساب النتيجة النهائية (بدون استراتيجيات أو حيتان)
271
- # الأوزان: مونت كارلو (40%)، الأنماط (30%)، المؤشرات (30%)
272
  components = []
273
  weights = []
274
 
@@ -290,14 +291,9 @@ class DataManager:
290
 
291
  async def layer1_rapid_screening(self) -> List[Dict[str, Any]]:
292
  """
293
- الطبقة 1: فحص سريع - (محدث بالكامل V7.1)
294
- 1. جلب أفضل 100 عملة حسب الحجم (24 ساعة).
295
- 2. جلب 100 شمعة (1H) لهذه الـ 100 عملة.
296
- 3. تشغيل "الكاشف المصغر" (مؤشرات، مونت كارلو، أنماط) على 1H.
297
- 4. استبعاد أي عملة درجتها < 0.20.
298
- 5. إرجاع العملات الناجحة فقط للطبقة 2.
299
  """
300
- print("📊 الطبقة 1 (V7.1): بدء الغربلة (الكاشف المصغر 1H)...")
301
 
302
  # الخطوة 1: جلب أفضل 100 عملة حسب الحجم
303
  volume_data = await self._get_volume_data_optimal()
@@ -315,7 +311,6 @@ class DataManager:
315
 
316
  final_candidates = []
317
 
318
- # (استخدام دفعات لمعالجة الـ 100 عملة بكفاءة)
319
  batch_size = 20
320
  for i in range(0, len(top_100_by_volume), batch_size):
321
  batch_symbols_data = top_100_by_volume[i:i + batch_size]
@@ -327,7 +322,6 @@ class DataManager:
327
  tasks = [self._fetch_1h_ohlcv_for_screening(symbol) for symbol in batch_symbols]
328
  results_candles = await asyncio.gather(*tasks, return_exceptions=True)
329
 
330
- # الخطوة 3 و 4: تطبيق الكاشف المصغر
331
  analysis_tasks = []
332
  valid_symbol_data = []
333
 
@@ -340,43 +334,45 @@ class DataManager:
340
 
341
  # (إعداد البيانات للمحللات)
342
  ohlcv_1h_only = {'1h': candles}
343
- symbol_data['ohlcv_1h'] = ohlcv_1h_only # (تخزين مؤقت)
344
- symbol_data['current_price'] = candles[-1][4] # آخر سعر إغلاق
345
  analysis_tasks.append(self._run_mini_detector(symbol_data))
346
  valid_symbol_data.append(symbol_data)
347
 
348
  if not analysis_tasks:
349
  continue
350
 
351
- # تشغيل التحليلات (MC, Patterns, Indicators) بالتوازي
352
  analysis_results = await asyncio.gather(*analysis_tasks, return_exceptions=True)
353
 
354
  for j, (analysis_output) in enumerate(analysis_results):
355
- symbol_data = valid_symbol_data[j] # (استرجاع بيانات الرمز المطابقة)
356
  symbol = symbol_data['symbol']
357
 
358
  if isinstance(analysis_output, Exception):
359
  print(f" - {symbol}: فشل الكاشف المصغر ({analysis_output})")
360
  continue
361
 
 
 
 
 
362
  # الخطوة 4: حساب النتيجة النهائية للفلتر
363
  filter_score = self._calculate_1h_filter_score(analysis_output)
364
 
365
  # الخطوة 5: تطبيق العتبة (Threshold)
366
  if filter_score >= 0.20:
367
  print(f" ✅ {symbol}: نجح (الدرجة: {filter_score:.2f})")
368
- # (إضافة البيانات الأولية للطبقة 2)
369
  symbol_data['layer1_score'] = filter_score
370
  symbol_data['reasons_for_candidacy'] = [f'1H_DETECTOR_PASS']
371
 
372
- # (تنظيف البيانات قبل إرسالها)
373
  if 'ohlcv_1h' in symbol_data: del symbol_data['ohlcv_1h']
374
 
375
  final_candidates.append(symbol_data)
376
  # else:
 
377
  # print(f" - {symbol}: فشل (الدرجة: {filter_score:.2f})")
378
 
379
- print(f"🎯 اكتملت الغربلة (V7.1). تم تأهيل {len(final_candidates)} عملة من أصل 100 لل��بقة 2.")
380
 
381
  print("🏆 المرشحون الناجحون:")
382
  for k, candidate in enumerate(final_candidates[:15]):
@@ -387,30 +383,24 @@ class DataManager:
387
  return final_candidates
388
 
389
  async def _run_mini_detector(self, symbol_data: Dict) -> Dict:
390
- """(جديد V7.1) يشغل المحللات الأساسية بالتوازي على بيانات 1H فقط"""
391
  ohlcv_1h = symbol_data.get('ohlcv_1h')
392
  current_price = symbol_data.get('current_price')
393
 
394
- # (إنشاء DataFrame مرة واحدة)
395
  df = self._create_dataframe(ohlcv_1h.get('1h'))
396
  if df.empty:
397
  raise ValueError("DataFrame فارغ لتحليل 1H")
398
 
399
  analysis_dict = {'current_price': current_price}
400
 
401
- # إنشاء المهام
402
- # (لا حاجة لـ asyncio.create_task لأن هذه الدوال ليست async)
403
  # (ملاحظة: جعلنا دوال التحليل async في ملفاتها لتكون متوافقة)
404
-
405
  task_indicators = self.technical_analyzer.calculate_all_indicators(df, '1h')
406
  task_mc = self.monte_carlo_analyzer.generate_1h_price_distribution(ohlcv_1h)
407
  task_pattern = self.pattern_analyzer.detect_chart_patterns(ohlcv_1h)
408
 
409
- # (تشغيل المهام التي هي async)
410
  results = await asyncio.gather(task_mc, task_pattern, return_exceptions=True)
411
 
412
- # (معالجة النتائج)
413
- analysis_dict['advanced_indicators'] = {'1h': task_indicators} # (هذه دالة متزامنة)
414
 
415
  if not isinstance(results[0], Exception):
416
  analysis_dict['monte_carlo_distribution'] = results[0]
@@ -421,20 +411,16 @@ class DataManager:
421
 
422
 
423
  async def _fetch_1h_ohlcv_for_screening(self, symbol: str) -> List:
424
- """(جديد V7.1) جلب 100 شمعة لإطار الساعة (1H) للغربلة السريعة"""
425
  try:
426
- # 100 شمعة كافية لحساب (RSI 14, EMA 21, MACD 26, BB 20)
427
  ohlcv_data = self.exchange.fetch_ohlcv(symbol, '1h', limit=100)
428
 
429
- if not ohlcv_data or len(ohlcv_data) < 50: # (الحد الأدنى 50)
430
  return None
431
  return ohlcv_data
432
  except Exception:
433
  return None
434
 
435
- # 🔴 --- END OF REFACTOR (V7.1) --- 🔴
436
-
437
-
438
  # (دوال جلب الحجم تبقى كما هي لأنها فعالة)
439
  async def _get_volume_data_optimal(self) -> List[Dict[str, Any]]:
440
  try:
@@ -453,8 +439,13 @@ class DataManager:
453
  if base_volume is None: continue
454
  dollar_volume = base_volume * current_price
455
  if dollar_volume is None or dollar_volume < 50000: continue
456
- price_change_24h = (ticker.get('percentage', 0) or 0) * 100
 
 
 
457
  if price_change_24h is None: price_change_24h = 0
 
 
458
  volume_data.append({
459
  'symbol': symbol, 'dollar_volume': dollar_volume,
460
  'current_price': current_price, 'volume_24h': ticker.get('baseVolume', 0) or 0,
@@ -488,7 +479,10 @@ class DataManager:
488
  if vol_value is None or last_price is None or change_rate is None or vol is None: continue
489
  dollar_volume = float(vol_value) if vol_value else 0
490
  current_price = float(last_price) if last_price else 0
 
 
491
  price_change = (float(change_rate) * 100) if change_rate else 0
 
492
  volume_24h = float(vol) if vol else 0
493
  if dollar_volume >= 50000 and current_price > 0:
494
  volume_data.append({
@@ -504,8 +498,9 @@ class DataManager:
504
  return []
505
 
506
  # (دالة تدفق الشموع تبقى كما هي - لا تغيير)
507
- async def stream_ohlcv_data(self, symbols: List[str], queue: asyncio.Queue):
508
  """
 
509
  جلب بيانات OHLCV كاملة (6 أطر زمنية) للعملات الناجحة فقط
510
  """
511
  print(f"📊 بدء تدفق بيانات OHLCV (الكاملة) لـ {len(symbols)} عملة (الناجحين من الغربلة)...")
@@ -519,28 +514,37 @@ class DataManager:
519
  print(f" 🔄 [المنتج] جلب الدفعة {batch_num + 1}/{len(batches)} ({len(batch)} عملة)...")
520
 
521
  batch_tasks = []
522
- for symbol in batch:
523
- task = asyncio.create_task(self._fetch_complete_ohlcv_parallel(symbol))
524
- batch_tasks.append(task)
525
 
 
 
 
 
 
 
 
 
526
  batch_results = await asyncio.gather(*batch_tasks, return_exceptions=True)
527
 
528
  successful_data_for_batch = []
529
  successful_count = 0
530
  for i, result in enumerate(batch_results):
531
- symbol = batch[i]
 
 
 
 
 
532
  if isinstance(result, Exception):
533
- print(f" ❌ [المنتج] فشل جلب {symbol}: {result}")
534
  elif result is not None:
535
- # (مهم: نمرر بيانات المرشح الأولية التي تحتوي على الدرجة)
536
- original_data = next((s for s in symbols if s['symbol'] == symbol), {'symbol': symbol})
537
- result.update(original_data) # (دمج الدرجة الأولية مع البيانات الكاملة)
538
  successful_data_for_batch.append(result)
539
  successful_count += 1
540
  timeframes_count = result.get('successful_timeframes', 0)
541
- print(f" ✅ [المنتج] {symbol}: {timeframes_count}/6 أطر زمنية")
542
  else:
543
- print(f" ⚠️ [المنتج] {symbol}: بيانات غير كافية، تم التجاهل")
544
 
545
  print(f" 📦 [المنتج] اكتملت الدفعة {batch_num + 1}: {successful_count}/{len(batch)} ناجحة")
546
 
@@ -565,7 +569,7 @@ class DataManager:
565
 
566
 
567
  async def _fetch_complete_ohlcv_parallel(self, symbol: str) -> Dict[str, Any]:
568
- """جلب بيانات OHLCV كاملة مع جميع الإطارات الزمنية بشكل متوازي"""
569
  try:
570
  ohlcv_data = {}
571
 
@@ -576,6 +580,7 @@ class DataManager:
576
 
577
  timeframe_tasks = []
578
  for timeframe, limit in timeframes:
 
579
  task = asyncio.create_task(self._fetch_single_timeframe_improved(symbol, timeframe, limit))
580
  timeframe_tasks.append(task)
581
 
@@ -614,11 +619,12 @@ class DataManager:
614
  except Exception as e: return None
615
 
616
  async def _fetch_single_timeframe_improved(self, symbol: str, timeframe: str, limit: int):
617
- """جلب بيانات إطار زمني واحد مع تحسين التعامل مع الأخطاء"""
618
  max_retries = 3
619
  retry_delay = 2
620
  for attempt in range(max_retries):
621
  try:
 
622
  ohlcv_data = self.exchange.fetch_ohlcv(symbol, timeframe, limit=limit)
623
  if ohlcv_data and len(ohlcv_data) > 0:
624
  return ohlcv_data
@@ -631,7 +637,7 @@ class DataManager:
631
  return []
632
 
633
  async def get_latest_price_async(self, symbol):
634
- """جلب السعر الحالي لعملة محددة"""
635
  try:
636
  if not self.exchange: return None
637
  if not symbol or '/' not in symbol: return None
@@ -660,4 +666,4 @@ class DataManager:
660
  except Exception as e:
661
  return {'action': 'HOLD', 'confidence': 0.3, 'reason': f'Error: {str(e)}', 'source': 'whale_analysis'}
662
 
663
- print("✅ DataManager loaded - V7.1 (1H Detector Screening)")
 
1
+ # data_manager.py (Updated to V7.2 - Fixed Task Bug & Added Stablecoin Guard)
2
  import os
3
  import asyncio
4
  import httpx
 
11
  from typing import List, Dict, Any
12
  import pandas as pd
13
 
 
 
14
  try:
15
  import pandas_ta as ta
16
  except ImportError:
17
  print("⚠️ مكتبة pandas_ta غير موجودة، فلتر الغربلة المتقدم سيفشل.")
18
  ta = None
19
 
 
20
  from ml_engine.indicators import AdvancedTechnicalAnalyzer
21
  from ml_engine.monte_carlo import MonteCarloAnalyzer
22
  from ml_engine.patterns import ChartPatternAnalyzer
 
23
 
24
  logging.getLogger("httpx").setLevel(logging.WARNING)
25
  logging.getLogger("httpcore").setLevel(logging.WARNING)
 
45
  self.market_cache = {}
46
  self.last_market_load = None
47
 
 
 
48
  self.technical_analyzer = AdvancedTechnicalAnalyzer()
49
  self.monte_carlo_analyzer = MonteCarloAnalyzer()
50
  self.pattern_analyzer = ChartPatternAnalyzer()
 
51
 
52
  async def initialize(self):
53
  self.http_client = httpx.AsyncClient(timeout=30.0)
54
  await self._load_markets()
55
+ print("✅ DataManager initialized - V7.2 (Stablecoin Guard Active)")
56
 
57
  async def _load_markets(self):
58
  try:
 
198
  'data_quality': 'LOW'
199
  }
200
 
201
+ # 🔴 --- START OF REFACTOR (V7.2 - Fixed Bugs) --- 🔴
202
 
203
  def _create_dataframe(self, candles: List) -> pd.DataFrame:
204
+ """(V7.1) دالة مساعدة لإنشاء DataFrame لتحليل 1H"""
205
  try:
206
  if not candles: return pd.DataFrame()
207
  df = pd.DataFrame(candles, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
 
216
 
217
  def _calculate_1h_filter_score(self, analysis: Dict) -> float:
218
  """
219
+ (محدث V7.2) - "الكاشف المصغر"
220
+ يحتوي الآن على "واقي العملات المستقرة"
 
221
  """
222
  try:
223
+ # 🔴 --- (FIX V7.2) واقي العملات المستقرة --- 🔴
224
+ # (يتم تشغيله قبل أي حساب آخر)
225
+ if 'ohlcv_1h' in analysis and '1h' in analysis['ohlcv_1h']:
226
+ closes_1h = [c[4] for c in analysis['ohlcv_1h']['1h']]
227
+ if len(closes_1h) > 20:
228
+ # (نستخدم آخر 20 شمعة للتحقق من التقلب)
229
+ std_dev = np.std(closes_1h[-20:])
230
+ if std_dev < 1e-5: # (تقلب شبه صفري)
231
+ print(f" - {analysis.get('symbol', 'N/A')}: تم الاستبعاد (عملة مستقرة)")
232
+ return 0.0 # (إعطاء درجة صفر فوراً)
233
+ # 🔴 --- نهاية الإصلاح --- 🔴
234
+
235
  # 1. درجة الأنماط (Pattern Score)
236
  pattern_confidence = analysis.get('pattern_analysis', {}).get('pattern_confidence', 0)
237
 
 
239
  mc_distribution = analysis.get('monte_carlo_distribution')
240
  monte_carlo_score = 0
241
 
242
+ if mc_distribution and mc_distribution.get('error') is None: # (تحقق من عدم وجود خطأ)
243
  prob_gain = mc_distribution.get('probability_of_gain', 0)
244
  var_95_value = mc_distribution.get('risk_metrics', {}).get('VaR_95_value', 0)
245
  current_price = analysis.get('current_price', 1)
 
250
  if normalized_var > 0.05: risk_penalty = 0.5
251
  elif normalized_var > 0.03: risk_penalty = 0.8
252
 
 
253
  normalized_prob_score = max(0.0, (prob_gain - 0.5) * 2)
254
  monte_carlo_score = normalized_prob_score * risk_penalty
255
 
256
  # 3. درجة المؤشرات (Indicator Score)
 
257
  indicator_score = 0
258
  indicators = analysis.get('advanced_indicators', {}).get('1h', {})
259
  if indicators:
 
270
  indicator_score = min(0.4 + (35 - rsi) / 35, 0.8)
271
 
272
  # 4. حساب النتيجة النهائية (بدون استراتيجيات أو حيتان)
 
273
  components = []
274
  weights = []
275
 
 
291
 
292
  async def layer1_rapid_screening(self) -> List[Dict[str, Any]]:
293
  """
294
+ الطبقة 1: فحص سريع - (محدث بالكامل V7.2)
 
 
 
 
 
295
  """
296
+ print("📊 الطبقة 1 (V7.2): بدء الغربلة (الكاشف المصغر 1H)...")
297
 
298
  # الخطوة 1: جلب أفضل 100 عملة حسب الحجم
299
  volume_data = await self._get_volume_data_optimal()
 
311
 
312
  final_candidates = []
313
 
 
314
  batch_size = 20
315
  for i in range(0, len(top_100_by_volume), batch_size):
316
  batch_symbols_data = top_100_by_volume[i:i + batch_size]
 
322
  tasks = [self._fetch_1h_ohlcv_for_screening(symbol) for symbol in batch_symbols]
323
  results_candles = await asyncio.gather(*tasks, return_exceptions=True)
324
 
 
325
  analysis_tasks = []
326
  valid_symbol_data = []
327
 
 
334
 
335
  # (إعداد البيانات للمحللات)
336
  ohlcv_1h_only = {'1h': candles}
337
+ symbol_data['ohlcv_1h'] = ohlcv_1h_only
338
+ symbol_data['current_price'] = candles[-1][4]
339
  analysis_tasks.append(self._run_mini_detector(symbol_data))
340
  valid_symbol_data.append(symbol_data)
341
 
342
  if not analysis_tasks:
343
  continue
344
 
 
345
  analysis_results = await asyncio.gather(*analysis_tasks, return_exceptions=True)
346
 
347
  for j, (analysis_output) in enumerate(analysis_results):
348
+ symbol_data = valid_symbol_data[j]
349
  symbol = symbol_data['symbol']
350
 
351
  if isinstance(analysis_output, Exception):
352
  print(f" - {symbol}: فشل الكاشف المصغر ({analysis_output})")
353
  continue
354
 
355
+ # (تمرير ohlcv_1h إلى دالة حساب النقاط من أجل واقي العملات المستقرة)
356
+ analysis_output['ohlcv_1h'] = symbol_data['ohlcv_1h']
357
+ analysis_output['symbol'] = symbol
358
+
359
  # الخطوة 4: حساب النتيجة النهائية للفلتر
360
  filter_score = self._calculate_1h_filter_score(analysis_output)
361
 
362
  # الخطوة 5: تطبيق العتبة (Threshold)
363
  if filter_score >= 0.20:
364
  print(f" ✅ {symbol}: نجح (الدرجة: {filter_score:.2f})")
 
365
  symbol_data['layer1_score'] = filter_score
366
  symbol_data['reasons_for_candidacy'] = [f'1H_DETECTOR_PASS']
367
 
 
368
  if 'ohlcv_1h' in symbol_data: del symbol_data['ohlcv_1h']
369
 
370
  final_candidates.append(symbol_data)
371
  # else:
372
+ # (تم إيقاف طباعة الفشل لتقليل التشويش)
373
  # print(f" - {symbol}: فشل (الدرجة: {filter_score:.2f})")
374
 
375
+ print(f"🎯 اكتملت الغربلة (V7.2). تم تأهيل {len(final_candidates)} عملة من أصل 100 للطبقة 2.")
376
 
377
  print("🏆 المرشحون الناجحون:")
378
  for k, candidate in enumerate(final_candidates[:15]):
 
383
  return final_candidates
384
 
385
  async def _run_mini_detector(self, symbol_data: Dict) -> Dict:
386
+ """(V7.1) يشغل المحللات الأساسية بالتوازي على بيانات 1H فقط"""
387
  ohlcv_1h = symbol_data.get('ohlcv_1h')
388
  current_price = symbol_data.get('current_price')
389
 
 
390
  df = self._create_dataframe(ohlcv_1h.get('1h'))
391
  if df.empty:
392
  raise ValueError("DataFrame فارغ لتحليل 1H")
393
 
394
  analysis_dict = {'current_price': current_price}
395
 
 
 
396
  # (ملاحظة: جعلنا دوال التحليل async في ملفاتها لتكون متوافقة)
 
397
  task_indicators = self.technical_analyzer.calculate_all_indicators(df, '1h')
398
  task_mc = self.monte_carlo_analyzer.generate_1h_price_distribution(ohlcv_1h)
399
  task_pattern = self.pattern_analyzer.detect_chart_patterns(ohlcv_1h)
400
 
 
401
  results = await asyncio.gather(task_mc, task_pattern, return_exceptions=True)
402
 
403
+ analysis_dict['advanced_indicators'] = {'1h': task_indicators}
 
404
 
405
  if not isinstance(results[0], Exception):
406
  analysis_dict['monte_carlo_distribution'] = results[0]
 
411
 
412
 
413
  async def _fetch_1h_ohlcv_for_screening(self, symbol: str) -> List:
414
+ """(V7.1) جلب 100 شمعة لإطار الساعة (1H) للغربلة السريعة"""
415
  try:
 
416
  ohlcv_data = self.exchange.fetch_ohlcv(symbol, '1h', limit=100)
417
 
418
+ if not ohlcv_data or len(ohlcv_data) < 50:
419
  return None
420
  return ohlcv_data
421
  except Exception:
422
  return None
423
 
 
 
 
424
  # (دوال جلب الحجم تبقى كما هي لأنها فعالة)
425
  async def _get_volume_data_optimal(self) -> List[Dict[str, Any]]:
426
  try:
 
439
  if base_volume is None: continue
440
  dollar_volume = base_volume * current_price
441
  if dollar_volume is None or dollar_volume < 50000: continue
442
+
443
+ # 🔴 --- (FIX V7.2) إصلاح نسبة التغير المئوية --- 🔴
444
+ # (ccxt 'percentage' هي النسبة الفعلية، لا تحتاج للضرب في 100)
445
+ price_change_24h = ticker.get('percentage', 0) or 0
446
  if price_change_24h is None: price_change_24h = 0
447
+ # 🔴 --- نهاية الإصلاح --- 🔴
448
+
449
  volume_data.append({
450
  'symbol': symbol, 'dollar_volume': dollar_volume,
451
  'current_price': current_price, 'volume_24h': ticker.get('baseVolume', 0) or 0,
 
479
  if vol_value is None or last_price is None or change_rate is None or vol is None: continue
480
  dollar_volume = float(vol_value) if vol_value else 0
481
  current_price = float(last_price) if last_price else 0
482
+
483
+ # (الإصلاح هنا أيضاً: 'changeRate' هو النسبة العشرية، لذا نضرب في 100)
484
  price_change = (float(change_rate) * 100) if change_rate else 0
485
+
486
  volume_24h = float(vol) if vol else 0
487
  if dollar_volume >= 50000 and current_price > 0:
488
  volume_data.append({
 
498
  return []
499
 
500
  # (دالة تدفق الشموع تبقى كما هي - لا تغيير)
501
+ async def stream_ohlcv_data(self, symbols: List[Dict[str, Any]], queue: asyncio.Queue):
502
  """
503
+ (محدث V7.2)
504
  جلب بيانات OHLCV كاملة (6 أطر زمنية) للعملات الناجحة فقط
505
  """
506
  print(f"📊 بدء تدفق بيانات OHLCV (الكاملة) لـ {len(symbols)} عملة (الناجحين من الغربلة)...")
 
514
  print(f" 🔄 [المنتج] جلب الدفعة {batch_num + 1}/{len(batches)} ({len(batch)} عملة)...")
515
 
516
  batch_tasks = []
 
 
 
517
 
518
+ # 🔴 --- (FIX V7.2) إصلاح تمرير الوسيط --- 🔴
519
+ for symbol_data in batch:
520
+ symbol_str = symbol_data['symbol'] # (استخراج النص)
521
+ # (تمرير النص فقط إلى الدالة التي تتوقع نصاً)
522
+ task = asyncio.create_task(self._fetch_complete_ohlcv_parallel(symbol_str))
523
+ batch_tasks.append(task)
524
+ # 🔴 --- نهاية الإصلاح --- 🔴
525
+
526
  batch_results = await asyncio.gather(*batch_tasks, return_exceptions=True)
527
 
528
  successful_data_for_batch = []
529
  successful_count = 0
530
  for i, result in enumerate(batch_results):
531
+
532
+ # 🔴 --- (FIX V7.2) مطابقة النتائج بالبيانات الأصلية --- 🔴
533
+ original_symbol_data = batch[i]
534
+ symbol_str = original_symbol_data['symbol']
535
+ # 🔴 --- نهاية الإصلاح --- 🔴
536
+
537
  if isinstance(result, Exception):
538
+ print(f" ❌ [المنتج] فشل جلب {symbol_str}: {result}")
539
  elif result is not None:
540
+ # (دمج الدرجة الأولية مع البيانات الكاملة)
541
+ result.update(original_symbol_data)
 
542
  successful_data_for_batch.append(result)
543
  successful_count += 1
544
  timeframes_count = result.get('successful_timeframes', 0)
545
+ print(f" ✅ [المنتج] {symbol_str}: {timeframes_count}/6 أطر زمنية")
546
  else:
547
+ print(f" ⚠️ [المنتج] {symbol_str}: بيانات غير كافية، تم التجاهل")
548
 
549
  print(f" 📦 [المنتج] اكتملت الدفعة {batch_num + 1}: {successful_count}/{len(batch)} ناجحة")
550
 
 
569
 
570
 
571
  async def _fetch_complete_ohlcv_parallel(self, symbol: str) -> Dict[str, Any]:
572
+ """(V7.2) جلب بيانات OHLCV كاملة - يتوقع 'symbol' كنص"""
573
  try:
574
  ohlcv_data = {}
575
 
 
580
 
581
  timeframe_tasks = []
582
  for timeframe, limit in timeframes:
583
+ # (هنا 'symbol' هو نص، وهو صحيح)
584
  task = asyncio.create_task(self._fetch_single_timeframe_improved(symbol, timeframe, limit))
585
  timeframe_tasks.append(task)
586
 
 
619
  except Exception as e: return None
620
 
621
  async def _fetch_single_timeframe_improved(self, symbol: str, timeframe: str, limit: int):
622
+ """(V7.2) جلب بيانات إطار زمني واحد - يتوقع 'symbol' كنص"""
623
  max_retries = 3
624
  retry_delay = 2
625
  for attempt in range(max_retries):
626
  try:
627
+ # (هنا 'symbol' هو نص، وهو صحيح)
628
  ohlcv_data = self.exchange.fetch_ohlcv(symbol, timeframe, limit=limit)
629
  if ohlcv_data and len(ohlcv_data) > 0:
630
  return ohlcv_data
 
637
  return []
638
 
639
  async def get_latest_price_async(self, symbol):
640
+ """(V7.2) جلب السعر الحالي - يتوقع 'symbol' كنص"""
641
  try:
642
  if not self.exchange: return None
643
  if not symbol or '/' not in symbol: return None
 
666
  except Exception as e:
667
  return {'action': 'HOLD', 'confidence': 0.3, 'reason': f'Error: {str(e)}', 'source': 'whale_analysis'}
668
 
669
+ print("✅ DataManager loaded - V7.2 (Stablecoin Guard Active + Task Bug Fixed)")