Riy777 commited on
Commit
5663c09
·
1 Parent(s): 3b15070

Update data_manager.py

Browse files
Files changed (1) hide show
  1. data_manager.py +327 -48
data_manager.py CHANGED
@@ -199,7 +199,6 @@ class DataManager:
199
  sentiment_task = asyncio.wait_for(self.get_sentiment_safe_async(), timeout=10)
200
  price_task = asyncio.wait_for(self._get_prices_with_fallback(), timeout=15)
201
 
202
- # ❌ إزالة مهمة الحيتان العامة
203
  results = await asyncio.gather(sentiment_task, price_task, return_exceptions=True)
204
 
205
  sentiment_data = results[0] if not isinstance(results[0], Exception) else None
@@ -217,7 +216,6 @@ class DataManager:
217
 
218
  market_trend = self._determine_market_trend(bitcoin_price, sentiment_data)
219
 
220
- # ❌ إزالة تحليل إشارات التداول المعتمدة على الحيتان العامة
221
  trading_decision = self._analyze_market_trading_signals(sentiment_data)
222
 
223
  market_context = {
@@ -226,7 +224,6 @@ class DataManager:
226
  'ethereum_price_usd': ethereum_price,
227
  'fear_and_greed_index': sentiment_data.get('feargreed_value') if sentiment_data else None,
228
  'sentiment_class': sentiment_data.get('feargreed_class') if sentiment_data else 'UNKNOWN',
229
- # ❌ إزالة بيانات الحيتان العامة
230
  'general_whale_activity': {
231
  'data_available': False,
232
  'description': 'نظام الحيتان العام معطل - يركز النظام على تحليل الحيتان للعملات المرشحة فقط',
@@ -240,11 +237,11 @@ class DataManager:
240
  'data_sources': {
241
  'prices': bitcoin_price is not None and ethereum_price is not None,
242
  'sentiment': sentiment_data is not None,
243
- 'general_whale_data': False, # ❌ دائماً false
244
- 'netflow_analysis': 'DISABLED' # ❌ معطل
245
  },
246
  'data_quality': 'HIGH',
247
- 'risk_assessment': self._assess_market_risk(sentiment_data) # ❌ إزالة الحيتان
248
  }
249
 
250
  return market_context
@@ -257,7 +254,7 @@ class DataManager:
257
  return self._get_minimal_market_context()
258
 
259
  def _analyze_market_trading_signals(self, sentiment_data):
260
- """تحليل إشارات التداول بناءً على مشاعر السوق فقط (بدون حيتان عامة)"""
261
  if not sentiment_data:
262
  return {
263
  'action': 'HOLD',
@@ -292,7 +289,7 @@ class DataManager:
292
  }
293
 
294
  def _assess_market_risk(self, sentiment_data):
295
- """تقييم مخاطر السوق بناءً على المشاعر فقط (بدون حيتان)"""
296
  risk_factors = []
297
  risk_score = 0
298
 
@@ -472,7 +469,6 @@ class DataManager:
472
  'last_market_load': self.last_market_load.isoformat() if self.last_market_load else None
473
  }
474
 
475
- # ❌ إزالة إحصائيات API للحيتان العامة
476
  stats['api_usage'] = {
477
  'note': 'نظام الحيتان العام معطل - يتم تحليل الحيتان للعملات المرشحة فقط'
478
  }
@@ -480,7 +476,7 @@ class DataManager:
480
  return stats
481
 
482
  async def get_symbol_specific_whale_data(self, symbol, contract_address=None):
483
- """جلب بيانات الحيتان الخاصة برمز معين (للمرشحين النهائيين فقط)"""
484
  if hasattr(self.whale_monitor, 'get_symbol_whale_activity'):
485
  return await self.whale_monitor.get_symbol_whale_activity(symbol, contract_address)
486
  else:
@@ -503,6 +499,14 @@ class DataManager:
503
  }
504
 
505
  async def _calculate_technical_score(self, symbol, ohlcv_data):
 
 
 
 
 
 
 
 
506
  try:
507
  if not ohlcv_data or '1h' not in ohlcv_data:
508
  return 0.0
@@ -515,14 +519,78 @@ class DataManager:
515
  volumes = np.array([candle[5] for candle in hourly_data])
516
  highs = np.array([candle[2] for candle in hourly_data])
517
  lows = np.array([candle[3] for candle in hourly_data])
 
518
 
519
- avg_volume = np.mean(volumes[-50:]) * np.mean(closes[-50:])
520
- liquidity_score = min(avg_volume / 1000000, 1.0)
 
 
521
 
522
- recent_volume = np.mean(volumes[-10:])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
  avg_volume_50 = np.mean(volumes[-50:])
524
  volume_ratio = recent_volume / avg_volume_50 if avg_volume_50 > 0 else 1.0
525
- volume_score = min(volume_ratio / 3.0, 1.0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
 
527
  true_ranges = []
528
  for i in range(1, len(hourly_data)):
@@ -532,33 +600,91 @@ class DataManager:
532
  tr3 = abs(low - prev_close)
533
  true_ranges.append(max(tr1, tr2, tr3))
534
 
535
- atr = np.mean(true_ranges[-14:]) if true_ranges else 0
536
- current_price = closes[-1]
537
- volatility_score = min((atr / current_price) * 100 / 10, 1.0)
 
 
 
 
 
 
 
 
 
 
 
538
 
539
- price_change_1h = ((closes[-1] - closes[-2]) / closes[-2]) * 100 if len(closes) > 1 else 0
540
- price_change_4h = ((closes[-1] - closes[-5]) / closes[-5]) * 100 if len(closes) > 5 else 0
541
- price_change_24h = ((closes[-1] - closes[-24]) / closes[-24]) * 100 if len(closes) > 24 else 0
 
 
 
 
 
 
 
 
 
 
542
 
543
- momentum_score = (abs(price_change_1h) + abs(price_change_4h) + abs(price_change_24h)) / 30
544
- momentum_score = min(momentum_score, 1.0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
 
546
  final_score = (
547
- liquidity_score * 0.3 +
548
- volume_score * 0.25 +
549
- volatility_score * 0.2 +
550
- momentum_score * 0.25
 
 
551
  )
552
 
553
- return final_score
 
 
 
 
554
 
555
  except Exception as e:
556
  print(f"❌ خطأ في حساب الدرجة التقنية لـ {symbol}: {e}")
557
  return 0.0
558
 
559
  async def find_high_potential_candidates(self, count=20):
 
 
 
 
 
 
 
560
  try:
561
- print(f"🔍 البحث عن {count} رمز ذو إمكانات عالية بناءً على المؤشرات التقنية...")
562
 
563
  if not self.exchange:
564
  print("❌ لا يوجد اتصال بـ KuCoin")
@@ -567,6 +693,7 @@ class DataManager:
567
  if not self.market_cache or not self.last_market_load or (datetime.now() - self.last_market_load).total_seconds() > 3600:
568
  await self._load_markets()
569
 
 
570
  usdt_symbols = [
571
  symbol for symbol in self.market_cache.keys()
572
  if symbol.endswith('/USDT') and self.market_cache[symbol].get('active', False)
@@ -574,63 +701,215 @@ class DataManager:
574
 
575
  print(f"✅ تم العثور على {len(usdt_symbols)} رمز USDT نشط في KuCoin")
576
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
  candidates_with_scores = []
578
  analyzed_count = 0
579
 
580
- for symbol in usdt_symbols[:100]:
 
 
 
581
  try:
 
582
  analyzed_count += 1
 
583
  if analyzed_count % 10 == 0:
584
- print(f"📊 تم تحليل {analyzed_count} رمز من أصل {min(100, len(usdt_symbols))}")
585
 
586
- ohlcv_1h = await self.exchange.fetch_ohlcv(symbol, '1h', limit=50)
 
587
 
588
- if not ohlcv_1h or len(ohlcv_1h) < 20:
589
  continue
590
 
591
  ohlcv_data = {'1h': ohlcv_1h}
592
 
 
593
  technical_score = await self._calculate_technical_score(symbol, ohlcv_data)
594
 
595
- if technical_score > 0.3:
596
- ticker = await self.exchange.fetch_ticker(symbol)
 
597
 
598
- reasons = []
599
  if technical_score > 0.7:
600
- reasons.append('high_technical_score')
601
- if ticker.get('baseVolume', 0) > 1000000:
 
 
 
 
 
 
 
 
 
 
 
602
  reasons.append('high_liquidity')
603
- if abs(ticker.get('percentage', 0)) > 5:
604
- reasons.append('significant_momentum')
605
 
606
  candidates_with_scores.append({
607
  'symbol': symbol,
608
  'technical_score': technical_score,
609
  'reasons': reasons,
610
- 'volume': ticker.get('baseVolume', 0),
611
- 'price_change': ticker.get('percentage', 0),
612
- 'current_price': ticker.get('last', 0)
 
 
613
  })
614
 
615
  except Exception as e:
616
  if "rate limit" not in str(e).lower():
617
- print(f"⚠️ خطأ في تحليل الرمز {symbol}: {e}")
618
  continue
619
 
 
 
 
 
 
 
 
 
620
  candidates_with_scores.sort(key=lambda x: x['technical_score'], reverse=True)
621
- top_candidates = candidates_with_scores[:count]
622
 
623
- print(f"✅ تم العثور على {len(top_candidates)} مرشح عالي الجودة من أصل {analyzed_count} رمز تم تحليله")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
624
 
625
- for candidate in top_candidates[:5]:
626
- print(f" 🥇 {candidate['symbol']}: درجة {candidate['technical_score']:.3f}")
627
 
628
- return top_candidates
 
 
 
 
629
 
630
  except Exception as e:
631
  print(f"❌ خطأ في find_high_potential_candidates: {e}")
 
 
632
  return []
633
 
 
 
 
 
 
 
 
 
 
634
  async def get_fast_pass_data_async(self, candidates):
635
  try:
636
  print(f"📊 جلب بيانات OHLCV لـ {len(candidates)} مرشح من KuCoin...")
 
199
  sentiment_task = asyncio.wait_for(self.get_sentiment_safe_async(), timeout=10)
200
  price_task = asyncio.wait_for(self._get_prices_with_fallback(), timeout=15)
201
 
 
202
  results = await asyncio.gather(sentiment_task, price_task, return_exceptions=True)
203
 
204
  sentiment_data = results[0] if not isinstance(results[0], Exception) else None
 
216
 
217
  market_trend = self._determine_market_trend(bitcoin_price, sentiment_data)
218
 
 
219
  trading_decision = self._analyze_market_trading_signals(sentiment_data)
220
 
221
  market_context = {
 
224
  'ethereum_price_usd': ethereum_price,
225
  'fear_and_greed_index': sentiment_data.get('feargreed_value') if sentiment_data else None,
226
  'sentiment_class': sentiment_data.get('feargreed_class') if sentiment_data else 'UNKNOWN',
 
227
  'general_whale_activity': {
228
  'data_available': False,
229
  'description': 'نظام الحيتان العام معطل - يركز النظام على تحليل الحيتان للعملات المرشحة فقط',
 
237
  'data_sources': {
238
  'prices': bitcoin_price is not None and ethereum_price is not None,
239
  'sentiment': sentiment_data is not None,
240
+ 'general_whale_data': False,
241
+ 'netflow_analysis': 'DISABLED'
242
  },
243
  'data_quality': 'HIGH',
244
+ 'risk_assessment': self._assess_market_risk(sentiment_data)
245
  }
246
 
247
  return market_context
 
254
  return self._get_minimal_market_context()
255
 
256
  def _analyze_market_trading_signals(self, sentiment_data):
257
+ """تحليل إشارات التداول بناءً على مشاعر السوق فقط"""
258
  if not sentiment_data:
259
  return {
260
  'action': 'HOLD',
 
289
  }
290
 
291
  def _assess_market_risk(self, sentiment_data):
292
+ """تقييم مخاطر السوق بناءً على المشاعر فقط"""
293
  risk_factors = []
294
  risk_score = 0
295
 
 
469
  'last_market_load': self.last_market_load.isoformat() if self.last_market_load else None
470
  }
471
 
 
472
  stats['api_usage'] = {
473
  'note': 'نظام الحيتان العام معطل - يتم تحليل الحيتان للعملات المرشحة فقط'
474
  }
 
476
  return stats
477
 
478
  async def get_symbol_specific_whale_data(self, symbol, contract_address=None):
479
+ """جلب بيانات الحيتان الخاصة برمز معين"""
480
  if hasattr(self.whale_monitor, 'get_symbol_whale_activity'):
481
  return await self.whale_monitor.get_symbol_whale_activity(symbol, contract_address)
482
  else:
 
499
  }
500
 
501
  async def _calculate_technical_score(self, symbol, ohlcv_data):
502
+ """
503
+ حساب درجة تقنية شاملة تعتمد على:
504
+ - الزخم السعري (قصير ومتوسط المدى)
505
+ - السيولة والحجم
506
+ - قوة الاتجاه
507
+ - التقلب
508
+ - الزخم الحجمي
509
+ """
510
  try:
511
  if not ohlcv_data or '1h' not in ohlcv_data:
512
  return 0.0
 
519
  volumes = np.array([candle[5] for candle in hourly_data])
520
  highs = np.array([candle[2] for candle in hourly_data])
521
  lows = np.array([candle[3] for candle in hourly_data])
522
+ opens = np.array([candle[1] for candle in hourly_data])
523
 
524
+ current_price = closes[-1]
525
+
526
+ # 1. تقييم الزخم السعري (Momentum) - الوزن الأكبر
527
+ momentum_score = 0.0
528
 
529
+ # زخم قصير المدى (1-4 ساعات)
530
+ if len(closes) >= 5:
531
+ price_change_1h = ((closes[-1] - closes[-2]) / closes[-2]) * 100
532
+ price_change_4h = ((closes[-1] - closes[-5]) / closes[-5]) * 100
533
+
534
+ # نفضل الزخم الإيجابي المعتدل (2%-10%)
535
+ if 2 <= price_change_1h <= 10:
536
+ momentum_score += 0.3
537
+ elif 2 <= price_change_4h <= 15:
538
+ momentum_score += 0.2
539
+
540
+ # زخم متوسط المدى (12-24 ساعة)
541
+ if len(closes) >= 25:
542
+ price_change_12h = ((closes[-1] - closes[-13]) / closes[-13]) * 100
543
+ price_change_24h = ((closes[-1] - closes[-25]) / closes[-25]) * 100
544
+
545
+ if 5 <= price_change_12h <= 20:
546
+ momentum_score += 0.2
547
+ if 8 <= price_change_24h <= 25:
548
+ momentum_score += 0.2
549
+
550
+ # 2. تقييم السيولة والحجم (Liquidity & Volume)
551
+ liquidity_score = 0.0
552
+
553
+ # متوسط الحجم بالدولار
554
+ avg_dollar_volume = np.mean(volumes[-50:]) * np.mean(closes[-50:])
555
+ if avg_dollar_volume > 1000000: # أكثر من 1 مليون دولار
556
+ liquidity_score += 0.4
557
+ elif avg_dollar_volume > 500000: # أكثر من 500 ألف دولار
558
+ liquidity_score += 0.3
559
+ elif avg_dollar_volume > 100000: # أكثر من 100 ألف دولار
560
+ liquidity_score += 0.2
561
+
562
+ # نسبة الحجم الحديث إلى المتوسط
563
+ recent_volume = np.mean(volumes[-6:]) # آخر 6 ساعات
564
  avg_volume_50 = np.mean(volumes[-50:])
565
  volume_ratio = recent_volume / avg_volume_50 if avg_volume_50 > 0 else 1.0
566
+
567
+ if volume_ratio > 1.5: # حجم مرتفع حديثاً
568
+ liquidity_score += 0.3
569
+ elif volume_ratio > 1.2:
570
+ liquidity_score += 0.2
571
+
572
+ # 3. تقييم قوة الاتجاه (Trend Strength)
573
+ trend_score = 0.0
574
+
575
+ if len(closes) >= 20:
576
+ # المتوسطات المتحركة
577
+ ma_short = np.mean(closes[-5:]) # 5 ساعات
578
+ ma_medium = np.mean(closes[-13:]) # 13 ساعة
579
+ ma_long = np.mean(closes[-21:]) # 21 ساعة
580
+
581
+ # تأكيد الاتجاه (جميع المتوسطات في نفس الاتجاه)
582
+ if ma_short > ma_medium > ma_long:
583
+ trend_score += 0.4 # اتجاه صاعد قوي
584
+ elif ma_short < ma_medium < ma_long:
585
+ trend_score += 0.2 # اتجاه هابط
586
+
587
+ # قوة الاتجاه (المسافة بين المتوسطات)
588
+ trend_strength = (ma_short - ma_long) / ma_long * 100
589
+ if abs(trend_strength) > 3: # اتجاه قوي (>3%)
590
+ trend_score += 0.2
591
+
592
+ # 4. تقييم التقلب (Volatility) - نفضل التقلب المعتدل
593
+ volatility_score = 0.0
594
 
595
  true_ranges = []
596
  for i in range(1, len(hourly_data)):
 
600
  tr3 = abs(low - prev_close)
601
  true_ranges.append(max(tr1, tr2, tr3))
602
 
603
+ if true_ranges:
604
+ atr = np.mean(true_ranges[-14:])
605
+ atr_percent = (atr / current_price) * 100
606
+
607
+ # نفضل التقلب بين 2% و 8% (فرص تداول جيدة)
608
+ if 2 <= atr_percent <= 8:
609
+ volatility_score = 0.3
610
+ elif atr_percent > 8:
611
+ volatility_score = 0.1 # تقلب عالي خطير
612
+ else:
613
+ volatility_score = 0.1 # تقلب منخفض قليل الفرص
614
+
615
+ # 5. تقييم الزخم الحجمي (Volume Momentum)
616
+ volume_momentum_score = 0.0
617
 
618
+ if len(volumes) >= 10:
619
+ # تسارع الحجم (الحجم الأخير vs المتوسط)
620
+ volume_acceleration = (volumes[-1] - np.mean(volumes[-10:])) / np.mean(volumes[-10:])
621
+ if volume_acceleration > 0.5: # زيادة حجم بنسبة 50%
622
+ volume_momentum_score = 0.2
623
+
624
+ # استمرارية الحجم المرتفع
625
+ above_avg_volume_count = sum(1 for vol in volumes[-5:] if vol > np.mean(volumes[-20:]))
626
+ if above_avg_volume_count >= 3: # 3 من آخر 5 ساعات فوق المتوسط
627
+ volume_momentum_score += 0.2
628
+
629
+ # 6. تقييم كفاءة الحركة (Price Efficiency)
630
+ efficiency_score = 0.0
631
 
632
+ if len(closes) >= 20:
633
+ # نسبة الشموع الخضراء
634
+ green_candles = sum(1 for i in range(1, len(closes)) if closes[i] > opens[i])
635
+ total_candles = len(closes) - 1
636
+ green_ratio = green_candles / total_candles if total_candles > 0 else 0
637
+
638
+ if 0.4 <= green_ratio <= 0.7: # توازن صحي
639
+ efficiency_score = 0.2
640
+
641
+ # قوة الشموع (متوسط طول الجسم)
642
+ candle_bodies = [abs(closes[i] - opens[i]) for i in range(len(closes))]
643
+ avg_body = np.mean(candle_bodies)
644
+ body_ratio = avg_body / current_price * 100
645
+
646
+ if 0.5 <= body_ratio <= 2: # شموع ذات أحجام معقولة
647
+ efficiency_score += 0.2
648
+
649
+ # الحساب النهائي مع الأوزان
650
+ weights = {
651
+ 'momentum': 0.30, # الزخم السعري
652
+ 'liquidity': 0.25, # السيولة والحجم
653
+ 'trend': 0.20, # قوة الاتجاه
654
+ 'volatility': 0.10, # التقلب
655
+ 'volume_momentum': 0.10, # الزخم الحجمي
656
+ 'efficiency': 0.05 # كفاءة الحركة
657
+ }
658
 
659
  final_score = (
660
+ momentum_score * weights['momentum'] +
661
+ liquidity_score * weights['liquidity'] +
662
+ trend_score * weights['trend'] +
663
+ volatility_score * weights['volatility'] +
664
+ volume_momentum_score * weights['volume_momentum'] +
665
+ efficiency_score * weights['efficiency']
666
  )
667
 
668
+ # معامل تصحيح إضافي للرموز ذات الزخم القوي
669
+ if momentum_score > 0.6 and volume_ratio > 1.8:
670
+ final_score = min(final_score * 1.2, 1.0)
671
+
672
+ return min(final_score, 1.0)
673
 
674
  except Exception as e:
675
  print(f"❌ خطأ في حساب الدرجة التقنية لـ {symbol}: {e}")
676
  return 0.0
677
 
678
  async def find_high_potential_candidates(self, count=20):
679
+ """
680
+ البحث عن عملات ذات إمكانات عالية بناءً على:
681
+ - الزخم السعري القوي والمستدام
682
+ - السيولة الجيدة (أحجام تداول عالية)
683
+ - اتجاهات تقنية واضحة
684
+ - مؤشرات حجم داعمة
685
+ """
686
  try:
687
+ print(f"🔍 البحث عن {count} عملة ذات إمكانات عالية بناءً على الزخم والسيولة...")
688
 
689
  if not self.exchange:
690
  print("❌ لا يوجد اتصال بـ KuCoin")
 
693
  if not self.market_cache or not self.last_market_load or (datetime.now() - self.last_market_load).total_seconds() > 3600:
694
  await self._load_markets()
695
 
696
+ # الحصول على جميع الرموز النشطة مع USDT
697
  usdt_symbols = [
698
  symbol for symbol in self.market_cache.keys()
699
  if symbol.endswith('/USDT') and self.market_cache[symbol].get('active', False)
 
701
 
702
  print(f"✅ تم العثور على {len(usdt_symbols)} رمز USDT نشط في KuCoin")
703
 
704
+ # المرحلة 1: الفحص السريع بناءً على بيانات Ticker
705
+ print("📊 المرحلة 1: الفحص السريع بناءً على أحجام التداول والزخم...")
706
+ initial_candidates = []
707
+
708
+ for i, symbol in enumerate(usdt_symbols):
709
+ try:
710
+ # الحصول على بيانات التداول الأساسية
711
+ ticker = await self.exchange.fetch_ticker(symbol)
712
+ if not ticker:
713
+ continue
714
+
715
+ volume_24h = ticker.get('baseVolume', 0)
716
+ price_change_24h = ticker.get('percentage', 0)
717
+ current_price = ticker.get('last', 0)
718
+
719
+ # معايير الفحص الأولي:
720
+ # - حجم تداول > 100,000 دولار
721
+ # - تغير سعري > 1% (إيجابي أو سلبي)
722
+ # - سعر بين 0.001 و 500 دولار (تجنب العملات منخفضة/مرتفعة السعر جداً)
723
+ if (volume_24h * current_price > 100000 and # حجم بالدولار
724
+ abs(price_change_24h) > 1.0 and # زخم سعري
725
+ 0.001 <= current_price <= 500): # نطاق سعري معقول
726
+
727
+ reasons = []
728
+ if price_change_24h > 5:
729
+ reasons.append('strong_positive_momentum')
730
+ elif price_change_24h > 2:
731
+ reasons.append('positive_momentum')
732
+
733
+ if volume_24h * current_price > 1000000:
734
+ reasons.append('high_liquidity')
735
+ elif volume_24h * current_price > 500000:
736
+ reasons.append('good_liquidity')
737
+
738
+ initial_candidates.append({
739
+ 'symbol': symbol,
740
+ 'volume_24h': volume_24h,
741
+ 'dollar_volume': volume_24h * current_price,
742
+ 'price_change_24h': price_change_24h,
743
+ 'current_price': current_price,
744
+ 'reasons': reasons
745
+ })
746
+
747
+ # عرض التقدم كل 100 رمز
748
+ if i % 100 == 0 and i > 0:
749
+ print(f" 🔍 تم فحص {i} رمز، وجدنا {len(initial_candidates)} مرشح أولي")
750
+
751
+ except Exception as e:
752
+ if "rate limit" not in str(e).lower():
753
+ continue
754
+
755
+ print(f"✅ انتهت المرحلة 1: تم اختيار {len(initial_candidates)} مرشح أولي")
756
+
757
+ # إذا لم نجد مرشحين كافيين، نخفف المعايير
758
+ if len(initial_candidates) < count * 2:
759
+ print("⚠️ عدد المرشحين قليل، نخفف معايير الفحص...")
760
+ for symbol in usdt_symbols:
761
+ if len(initial_candidates) >= count * 3:
762
+ break
763
+
764
+ if symbol in [c['symbol'] for c in initial_candidates]:
765
+ continue
766
+
767
+ try:
768
+ ticker = await self.exchange.fetch_ticker(symbol)
769
+ if not ticker:
770
+ continue
771
+
772
+ volume_24h = ticker.get('baseVolume', 0)
773
+ price_change_24h = ticker.get('percentage', 0)
774
+ current_price = ticker.get('last', 0)
775
+
776
+ # معايير مخففة
777
+ if (volume_24h * current_price > 50000 and # حجم بالدولار
778
+ abs(price_change_24h) > 0.5 and # زخم سعري
779
+ 0.0005 <= current_price <= 1000): # نطاق سعري أوسع
780
+
781
+ reasons = ['relaxed_criteria']
782
+ if price_change_24h > 0:
783
+ reasons.append('positive_trend')
784
+
785
+ initial_candidates.append({
786
+ 'symbol': symbol,
787
+ 'volume_24h': volume_24h,
788
+ 'dollar_volume': volume_24h * current_price,
789
+ 'price_change_24h': price_change_24h,
790
+ 'current_price': current_price,
791
+ 'reasons': reasons
792
+ })
793
+
794
+ except Exception:
795
+ continue
796
+
797
+ print(f"📊 بعد التخفيف: {len(initial_candidates)} مرشح")
798
+
799
+ # المرحلة 2: التحليل التقني المتعمق
800
+ print("📈 المرحلة 2: التحليل التقني المتعمق للمرشحين...")
801
  candidates_with_scores = []
802
  analyzed_count = 0
803
 
804
+ # نرتب المرشحين بحجم التداول (الأكبر أولاً)
805
+ initial_candidates.sort(key=lambda x: x['dollar_volume'], reverse=True)
806
+
807
+ for candidate in initial_candidates[:100]: # نأخذ أفضل 100 من حيث الحجم
808
  try:
809
+ symbol = candidate['symbol']
810
  analyzed_count += 1
811
+
812
  if analyzed_count % 10 == 0:
813
+ print(f" 🔍 تحليل تقني {analyzed_count} من {min(100, len(initial_candidates))}")
814
 
815
+ # جلب بيانات OHLCV للتحليل التقني
816
+ ohlcv_1h = await self.exchange.fetch_ohlcv(symbol, '1h', limit=100)
817
 
818
+ if not ohlcv_1h or len(ohlcv_1h) < 50:
819
  continue
820
 
821
  ohlcv_data = {'1h': ohlcv_1h}
822
 
823
+ # حساب الدرجة التقنية الشاملة
824
  technical_score = await self._calculate_technical_score(symbol, ohlcv_data)
825
 
826
+ if technical_score > 0.3: # عتبة مقبولة للجودة
827
+ # تحديث أسباب الترشيح بناءً على التحليل التقني
828
+ reasons = candidate['reasons']
829
 
 
830
  if technical_score > 0.7:
831
+ reasons.append('excellent_technical_score')
832
+ elif technical_score > 0.5:
833
+ reasons.append('good_technical_score')
834
+
835
+ # إضافة أسباب تقنية
836
+ if candidate['price_change_24h'] > 8:
837
+ reasons.append('strong_daily_momentum')
838
+ elif candidate['price_change_24h'] > 3:
839
+ reasons.append('moderate_daily_momentum')
840
+
841
+ if candidate['dollar_volume'] > 2000000:
842
+ reasons.append('very_high_liquidity')
843
+ elif candidate['dollar_volume'] > 500000:
844
  reasons.append('high_liquidity')
 
 
845
 
846
  candidates_with_scores.append({
847
  'symbol': symbol,
848
  'technical_score': technical_score,
849
  'reasons': reasons,
850
+ 'volume': candidate['volume_24h'],
851
+ 'dollar_volume': candidate['dollar_volume'],
852
+ 'price_change': candidate['price_change_24h'],
853
+ 'current_price': candidate['current_price'],
854
+ 'market_cap_category': self._classify_market_cap(candidate['dollar_volume'])
855
  })
856
 
857
  except Exception as e:
858
  if "rate limit" not in str(e).lower():
859
+ print(f"⚠️ خطأ في التحليل التقني للرمز {symbol}: {e}")
860
  continue
861
 
862
+ print(f"✅ انتهت المرحلة 2: {len(candidates_with_scores)} مرشح اجتازوا التحليل التقني")
863
+
864
+ # المرحلة 3: الاختيار النهائي مع التنوع
865
+ if not candidates_with_scores:
866
+ print("❌ لم يتم العثور على أي مرشح يلبي المعايير")
867
+ return []
868
+
869
+ # ترتيب المرشحين حسب الدرجة التقنية
870
  candidates_with_scores.sort(key=lambda x: x['technical_score'], reverse=True)
 
871
 
872
+ # نأخذ أفضل المرشحين مع الحفاظ على التنوع
873
+ final_candidates = []
874
+ categories_count = {'LARGE': 0, 'MID': 0, 'SMALL': 0}
875
+ max_per_category = max(1, count // 3)
876
+
877
+ for candidate in candidates_with_scores:
878
+ category = candidate['market_cap_category']
879
+
880
+ if categories_count[category] < max_per_category:
881
+ final_candidates.append(candidate)
882
+ categories_count[category] += 1
883
+ elif len(final_candidates) < count:
884
+ final_candidates.append(candidate)
885
+
886
+ if len(final_candidates) >= count:
887
+ break
888
 
889
+ print(f"🎯 الاختيار النهائي: {len(final_candidates)} مرشح متنوع")
890
+ print(f" 📊 تصنيف الأحجام: Large({categories_count['LARGE']}), Mid({categories_count['MID']}), Small({categories_count['SMALL']})")
891
 
892
+ # عرض أفضل 5 مرشحين
893
+ for i, candidate in enumerate(final_candidates[:5]):
894
+ print(f" 🥇 {i+1}. {candidate['symbol']}: درجة {candidate['technical_score']:.3f} - تغير {candidate['price_change']:.1f}% - حجم ${candidate['dollar_volume']:,.0f}")
895
+
896
+ return final_candidates
897
 
898
  except Exception as e:
899
  print(f"❌ خطأ في find_high_potential_candidates: {e}")
900
+ import traceback
901
+ traceback.print_exc()
902
  return []
903
 
904
+ def _classify_market_cap(self, dollar_volume):
905
+ """تصنيف العملات حسب حجم التداول"""
906
+ if dollar_volume > 5000000: # أكثر من 5 ملايين دولار
907
+ return "LARGE"
908
+ elif dollar_volume > 1000000: # أكثر من 1 مليون دولار
909
+ return "MID"
910
+ else: # أقل من 1 مليون دولار
911
+ return "SMALL"
912
+
913
  async def get_fast_pass_data_async(self, candidates):
914
  try:
915
  print(f"📊 جلب بيانات OHLCV لـ {len(candidates)} مرشح من KuCoin...")