Riy777 commited on
Commit
24a0949
·
1 Parent(s): 925bbcf

Update data_manager.py

Browse files
Files changed (1) hide show
  1. data_manager.py +311 -828
data_manager.py CHANGED
@@ -8,8 +8,8 @@ from datetime import datetime
8
  import ccxt.pro as ccxt
9
  import numpy as np
10
  import logging
 
11
 
12
- # تعطيل تسجيل HTTP المزعج فقط
13
  logging.getLogger("httpx").setLevel(logging.WARNING)
14
  logging.getLogger("httpcore").setLevel(logging.WARNING)
15
 
@@ -31,33 +31,15 @@ class DataManager:
31
  print(f"❌ فشل تهيئة اتصال KuCoin: {e}")
32
  self.exchange = None
33
 
34
- self._whale_data_cache = {}
35
  self.http_client = None
36
- self.fetch_stats = {'successful_fetches': 0, 'failed_fetches': 0, 'rate_limit_hits': 0}
37
- self.price_cache = {}
38
  self.market_cache = {}
39
  self.last_market_load = None
40
 
41
- self.price_sources = {
42
- 'kucoin': self._get_prices_from_kucoin_safe,
43
- 'coingecko': self._get_prices_from_coingecko
44
- }
45
-
46
  async def initialize(self):
47
  self.http_client = httpx.AsyncClient(timeout=20.0)
48
-
49
- api_status = {
50
- 'KUCOIN': '🟢 عام (بدون مفتاح)',
51
- 'MORALIS_KEY': "🟢 متوفر" if os.getenv('MORALIS_KEY') else "🔴 غير متوفر",
52
- 'ETHERSCAN_KEY': "🟢 متوفر" if os.getenv('ETHERSCAN_KEY') else "🔴 غير متوفر",
53
- 'INFURA_KEY': "🟢 متوفر" if os.getenv('INFURA_KEY') else "🔴 غير متوفر"
54
- }
55
-
56
- for key, status in api_status.items():
57
- print(f" {key}: {status}")
58
-
59
  await self._load_markets()
60
-
 
61
  async def _load_markets(self):
62
  try:
63
  if not self.exchange:
@@ -78,232 +60,78 @@ class DataManager:
78
  if self.exchange:
79
  await self.exchange.close()
80
 
81
- async def get_native_coin_price(self, network):
82
- now = time.time()
83
- cache_key = f"{network}_price"
84
-
85
- if cache_key in self.price_cache and (now - self.price_cache[cache_key]['timestamp']) < 300:
86
- return self.price_cache[cache_key]['price']
87
-
88
- symbol_map = {
89
- 'ethereum': 'ETH',
90
- 'bsc': 'BNB',
91
- 'bitcoin': 'BTC'
92
- }
93
-
94
- symbol = symbol_map.get(network)
95
- if not symbol:
96
- return await self._get_price_from_coingecko_fallback(network)
97
-
98
- try:
99
- price = await self._get_price_from_kucoin(symbol)
100
- if price and price > 0:
101
- self.price_cache[cache_key] = {'price': price, 'timestamp': now, 'source': 'kucoin'}
102
- return price
103
-
104
- price = await self._get_price_from_coingecko_fallback(network)
105
- if price and price > 0:
106
- self.price_cache[cache_key] = {'price': price, 'timestamp': now, 'source': 'coingecko'}
107
- return price
108
-
109
- return None
110
-
111
- except Exception as e:
112
- print(f"❌ فشل جلب سعر {network}: {e}")
113
- return None
114
-
115
- async def _get_price_from_kucoin(self, symbol):
116
- if not self.exchange:
117
- return None
118
-
119
  try:
120
- trading_symbol = f"{symbol}/USDT"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
- if trading_symbol not in self.market_cache:
123
- print(f"⚠️ السوق {trading_symbol} غير متوفر في KuCoin")
124
- return None
125
 
126
- ticker = await self.exchange.fetch_ticker(trading_symbol)
127
- price = ticker.get('last')
128
- if price and price > 0:
129
- return float(price)
130
- else:
131
- print(f"⚠️ لم يتم العثور على سعر صالح لـ {symbol}")
132
- return None
133
-
134
  except Exception as e:
135
- print(f"❌ فشل جلب سعر {symbol} من KuCoin: {e}")
136
- return None
137
-
138
- async def _get_price_from_coingecko_fallback(self, network):
139
- coin_map = {
140
- 'ethereum': 'ethereum',
141
- 'bsc': 'binancecoin',
142
- 'bitcoin': 'bitcoin'
143
- }
144
-
145
- coin_id = coin_map.get(network)
146
- if not coin_id:
147
- return None
148
 
 
 
149
  try:
150
- await asyncio.sleep(0.5)
151
- url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies=usd"
152
- async with httpx.AsyncClient() as client:
153
- response = await client.get(url, timeout=10)
154
  response.raise_for_status()
155
  data = response.json()
156
 
157
- price = data.get(coin_id, {}).get('usd')
158
- if price and price > 0:
159
- print(f"✅ سعر {network.upper()} (CoinGecko): ${price:,.2f}")
160
- return price
161
- else:
162
- print(f"⚠️ لم يتم العثور على سعر {network} في CoinGecko")
163
- return None
164
-
165
- except Exception as e:
166
- print(f"❌ فشل جلب سعر {network} من CoinGecko: {e}")
167
- return None
168
-
169
- async def get_sentiment_safe_async(self):
170
- max_retries = 2
171
- for attempt in range(max_retries):
172
- try:
173
- async with httpx.AsyncClient(timeout=8) as client:
174
- response = await client.get("https://api.alternative.me/fng/")
175
- response.raise_for_status()
176
- data = response.json()
177
-
178
- if 'data' not in data or not data['data']:
179
- raise ValueError("بيانات المشاعر غير متوفرة في الاستجابة")
180
-
181
- latest_data = data['data'][0]
182
- return {
183
- "feargreed_value": int(latest_data['value']),
184
- "feargreed_class": latest_data['value_classification'],
185
- "source": "alternative.me",
186
- "timestamp": datetime.now().isoformat()
187
- }
188
- except Exception as e:
189
- print(f"❌ فشل جلب بيانات المشاعر (المحاولة {attempt + 1}): {e}")
190
- if attempt < max_retries - 1:
191
- await asyncio.sleep(1)
192
-
193
- return None
194
-
195
- async def get_market_context_async(self):
196
- max_retries = 2
197
- for attempt in range(max_retries):
198
- try:
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
205
- price_data = results[1] if not isinstance(results[1], Exception) else {}
206
-
207
- bitcoin_price = price_data.get('bitcoin')
208
- ethereum_price = price_data.get('ethereum')
209
 
210
- if bitcoin_price is None or ethereum_price is None:
211
- if attempt < max_retries - 1:
212
- await asyncio.sleep(2)
213
- continue
214
- else:
215
- return self._get_minimal_market_context()
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 = {
222
- 'timestamp': datetime.now().isoformat(),
223
- 'bitcoin_price_usd': bitcoin_price,
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': 'نظام الحيتان العام معطل - يركز النظام على تحليل الحيتان للعملات المرشحة فقط',
230
- 'critical_alert': False,
231
- 'sentiment': 'NEUTRAL',
232
- 'trading_signals': []
233
- },
234
- 'market_trend': market_trend,
235
- 'trading_decision': trading_decision,
236
- 'btc_sentiment': self._get_btc_sentiment(bitcoin_price),
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
248
-
249
- except Exception as e:
250
- print(f"❌ فشل جلب سياق السوق (المحاولة {attempt + 1}): {e}")
251
- if attempt < max_retries - 1:
252
- await asyncio.sleep(3)
253
-
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',
261
- 'confidence': 0.0,
262
- 'reason': 'غير متوفر - لا توجد بيانات كافية عن مشاعر السوق',
263
- 'risk_level': 'UNKNOWN'
264
- }
265
-
266
- fear_greed = sentiment_data.get('feargreed_value', 50)
267
- sentiment_class = sentiment_data.get('feargreed_class', 'NEUTRAL')
268
 
269
- if fear_greed <= 25:
270
- return {
271
- 'action': 'BUY',
272
- 'confidence': 0.7,
273
- 'reason': f'مستوى خوف مرتفع في السوق: {fear_greed} - فرصة شرائية',
274
- 'risk_level': 'LOW'
275
- }
276
- elif fear_greed >= 75:
277
- return {
278
- 'action': 'SELL',
279
- 'confidence': 0.6,
280
- 'reason': f'مستوى جشع مرتفع في السوق: {fear_greed} - احتياط بيعي',
281
- 'risk_level': 'MEDIUM'
282
- }
283
- else:
284
- return {
285
- 'action': 'HOLD',
286
- 'confidence': 0.5,
287
- 'reason': f'مشاعر السوق متوازنة: {sentiment_class} ({fear_greed})',
288
- 'risk_level': 'LOW'
289
- }
290
-
291
- def _assess_market_risk(self, sentiment_data):
292
- """تقييم مخاطر السوق بناءً على المشاعر فقط"""
293
- risk_factors = []
294
- risk_score = 0
295
 
296
- if sentiment_data and sentiment_data.get('feargreed_value', 50) < 30:
297
- risk_factors.append("مخاوف السوق عالية")
298
- risk_score += 2
299
- elif sentiment_data and sentiment_data.get('feargreed_value', 50) > 70:
300
- risk_factors.append("جشع السوق مرتفع")
301
- risk_score += 1
302
 
303
- if risk_score >= 2:
304
- return {'level': 'MEDIUM', 'score': risk_score, 'factors': risk_factors}
 
 
305
  else:
306
- return {'level': 'LOW', 'score': risk_score, 'factors': risk_factors}
307
 
308
  def _get_btc_sentiment(self, bitcoin_price):
309
  if bitcoin_price is None:
@@ -316,17 +144,12 @@ class DataManager:
316
  return 'NEUTRAL'
317
 
318
  async def _get_prices_with_fallback(self):
 
319
  try:
320
  prices = await self._get_prices_from_kucoin_safe()
321
  if prices.get('bitcoin') and prices.get('ethereum'):
322
  return prices
323
-
324
- prices = await self._get_prices_from_coingecko()
325
- if prices.get('bitcoin') and prices.get('ethereum'):
326
- return prices
327
-
328
- return {'bitcoin': None, 'ethereum': None}
329
-
330
  except Exception as e:
331
  print(f"❌ فشل جلب الأسعار: {e}")
332
  return {'bitcoin': None, 'ethereum': None}
@@ -338,37 +161,24 @@ class DataManager:
338
  try:
339
  prices = {'bitcoin': None, 'ethereum': None}
340
 
341
- try:
342
- btc_ticker = await self.exchange.fetch_ticker('BTC/USDT')
343
- btc_price = float(btc_ticker.get('last', 0)) if btc_ticker.get('last') else None
344
- if btc_price and btc_price > 0:
345
- prices['bitcoin'] = btc_price
346
- self.price_cache['bitcoin'] = btc_price
347
- print(f"✅ سعر BTC: ${btc_price:,.2f}")
348
- else:
349
- print("⚠️ لم يتم العثور على سعر BTC صالح")
350
- except Exception as e:
351
- print(f"❌ فشل جلب سعر BTC: {e}")
352
 
353
- try:
354
- eth_ticker = await self.exchange.fetch_ticker('ETH/USDT')
355
- eth_price = float(eth_ticker.get('last', 0)) if eth_ticker.get('last') else None
356
- if eth_price and eth_price > 0:
357
- prices['ethereum'] = eth_price
358
- self.price_cache['ethereum'] = eth_price
359
- print(f"✅ سعر ETH: ${eth_price:,.2f}")
360
- else:
361
- print("⚠️ لم يتم العثور على سعر ETH صالح")
362
- except Exception as e:
363
- print(f"❌ فشل جلب سعر ETH: {e}")
364
 
365
  return prices
366
 
367
  except Exception as e:
368
- print(f"❌ خطأ في _get_prices_from_kucoin_safe: {e}")
369
  return {'bitcoin': None, 'ethereum': None}
370
 
371
  async def _get_prices_from_coingecko(self):
 
372
  try:
373
  await asyncio.sleep(0.5)
374
  url = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd"
@@ -381,13 +191,8 @@ class DataManager:
381
  eth_price = data.get('ethereum', {}).get('usd')
382
 
383
  if btc_price and eth_price:
384
- self.price_cache['bitcoin'] = btc_price
385
- self.price_cache['ethereum'] = eth_price
386
- print(f"✅ سعر BTC (CoinGecko): ${btc_price:,.2f}")
387
- print(f"✅ سعر ETH (CoinGecko): ${eth_price:,.2f}")
388
  return {'bitcoin': btc_price, 'ethereum': eth_price}
389
  else:
390
- print("⚠️ لم يتم العثور على أسعار صالحة من CoinGecko")
391
  return {'bitcoin': None, 'ethereum': None}
392
 
393
  except Exception as e:
@@ -395,593 +200,270 @@ class DataManager:
395
  return {'bitcoin': None, 'ethereum': None}
396
 
397
  def _get_minimal_market_context(self):
 
398
  return {
399
  'timestamp': datetime.now().isoformat(),
400
  'data_available': False,
401
- 'data_sources': {'prices': False, 'sentiment': False, 'general_whale_data': False},
402
- 'error': 'غير متوفر - فشل في جلب بيانات السوق من المصادر الخارجية',
403
  'market_trend': 'UNKNOWN',
404
  'btc_sentiment': 'UNKNOWN',
405
- 'data_quality': 'LOW',
406
- 'general_whale_activity': {
407
- 'data_available': False,
408
- 'description': 'نظام الحيتان العام معطل - يركز النظام على تحليل الحيتان للعملات المرشحة فقط',
409
- 'critical_alert': False,
410
- 'sentiment': 'NEUTRAL'
411
- },
412
- 'bitcoin_price_usd': None,
413
- 'ethereum_price_usd': None,
414
- 'fear_and_greed_index': None,
415
- 'sentiment_class': 'UNKNOWN',
416
- 'missing_data': ['غير متوفر - أسعار البيتكوين', 'غير متوفر - أسعار الإيثيريوم', 'غير متوفر - بيانات المشاعر']
417
  }
418
 
419
- def _determine_market_trend(self, bitcoin_price, sentiment_data):
420
- """تحديد اتجاه السوق بدون بيانات الحيتان العامة"""
421
- try:
422
- if bitcoin_price is None:
423
- return "UNKNOWN"
424
-
425
- score = 0
426
- data_points = 1
 
 
 
 
 
 
 
 
 
 
 
427
 
428
- if bitcoin_price > 60000:
429
- score += 1
430
- elif bitcoin_price < 55000:
431
- score -= 1
432
 
433
- if sentiment_data and sentiment_data.get('feargreed_value') is not None:
434
- fear_greed = sentiment_data.get('feargreed_value')
435
- if fear_greed > 60:
436
- score += 1
437
- elif fear_greed < 40:
438
- score -= 1
439
- data_points += 1
440
 
441
- if data_points < 2:
442
- return "UNKNOWN"
443
-
444
- if score >= 2:
445
- return "bull_market"
446
- elif score <= -2:
447
- return "bear_market"
448
- elif -1 <= score <= 1:
449
- return "sideways_market"
450
- else:
451
- return "volatile_market"
452
-
453
- except Exception as e:
454
- return "UNKNOWN"
455
-
456
- def get_performance_stats(self):
457
- total_attempts = self.fetch_stats['successful_fetches'] + self.fetch_stats['failed_fetches']
458
- success_rate = (self.fetch_stats['successful_fetches'] / total_attempts * 100) if total_attempts > 0 else 0
459
 
460
- stats = {
461
- 'total_attempts': total_attempts,
462
- 'successful_fetches': self.fetch_stats['successful_fetches'],
463
- 'failed_fetches': self.fetch_stats['failed_fetches'],
464
- 'rate_limit_hits': self.fetch_stats['rate_limit_hits'],
465
- 'success_rate': f"{success_rate:.1f}%",
466
- 'timestamp': datetime.now().isoformat(),
467
- 'exchange_available': self.exchange is not None,
468
- 'markets_loaded': len(self.market_cache) if self.market_cache else 0,
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
- }
 
 
 
 
 
 
475
 
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:
483
- return {
484
- 'data_available': False,
485
- 'error': 'نظام تحليل الحيتان غير متوفر',
486
- 'symbol': symbol
487
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
488
 
489
- async def get_whale_trading_signal(self, symbol, whale_data, market_context):
490
- """جلب إشارة تداول بناءً على بيانات الحيتان للرمز المحدد"""
491
- if hasattr(self.whale_monitor, 'generate_whale_trading_signal'):
492
- return await self.whale_monitor.generate_whale_trading_signal(symbol, whale_data, market_context)
 
 
 
 
 
 
 
493
  else:
494
- return {
495
- 'action': 'HOLD',
496
- 'confidence': 0.3,
497
- 'reason': 'نظام تحليل الحيتان غير متوفر',
498
- 'source': 'whale_analysis'
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
513
-
514
- hourly_data = ohlcv_data['1h']
515
- if len(hourly_data) < 50:
516
- return 0.0
517
-
518
- closes = np.array([candle[4] for candle in hourly_data])
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)):
597
- high, low, prev_close = highs[i], lows[i], closes[i-1]
598
- tr1 = high - low
599
- tr2 = abs(high - prev_close)
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")
691
- return []
692
-
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)
700
- ]
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...")
916
- results = []
917
-
918
- timeframes = [
919
- ('5m', 200),
920
- ('15m', 200),
921
- ('1h', 200),
922
- ('4h', 200),
923
- ('1d', 200),
924
- ('1w', 200)
925
- ]
926
-
927
- for candidate in candidates:
928
- symbol = candidate['symbol']
929
-
930
- try:
931
- ohlcv_data = {}
932
- has_sufficient_data = True
933
-
934
- for timeframe, limit in timeframes:
935
- try:
936
- ohlcv = await self.exchange.fetch_ohlcv(symbol, timeframe, limit=limit)
937
-
938
- if not ohlcv or len(ohlcv) < 50:
939
- print(f"⚠️ بيانات غير كافية للرمز {symbol} في الإطار {timeframe}: {len(ohlcv) if ohlcv else 0} شمعة")
940
- has_sufficient_data = False
941
- break
942
-
943
  ohlcv_data[timeframe] = ohlcv
944
- print(f" ✅ {symbol} - {timeframe}: {len(ohlcv)} شمعة")
945
-
946
- await asyncio.sleep(0.1)
947
-
948
- except Exception as e:
949
- print(f"❌ خطأ في جلب بيانات {symbol} للإطار {timeframe}: {e}")
950
  has_sufficient_data = False
951
  break
 
 
 
 
 
 
 
 
952
 
953
- if has_sufficient_data:
954
- result_data = {
955
- 'symbol': symbol,
956
- 'ohlcv': ohlcv_data,
957
- 'reasons': candidate.get('reasons', []),
958
- 'technical_score': candidate.get('technical_score', 0)
959
- }
960
-
961
- results.append(result_data)
962
- print(f"✅ تم تجميع بيانات {symbol} بنجاح")
963
- else:
964
- print(f"❌ فشل تجميع بيانات كافية لـ {symbol}")
965
-
966
- except Exception as symbol_error:
967
- print(f" خطأ في معالجة الرمز {symbol}: {symbol_error}")
968
- continue
969
-
970
- print(f"✅ تم تجميع بيانات لـ {len(results)} مرشح بنجاح")
971
- return results
972
-
973
- except Exception as e:
974
- print(f"❌ خطأ في get_fast_pass_data_async: {e}")
975
- return []
976
 
977
  async def get_latest_price_async(self, symbol):
 
978
  try:
979
  if not self.exchange:
980
- print("❌ لا يوجد اتصال بـ KuCoin")
981
- return None
982
-
983
- if symbol not in self.market_cache:
984
- print(f"⚠️ السوق {symbol} غير متوفر في KuCoin")
985
  return None
986
 
987
  ticker = await self.exchange.fetch_ticker(symbol)
@@ -990,14 +472,14 @@ class DataManager:
990
  if current_price:
991
  return float(current_price)
992
  else:
993
- print(f"❌ لم يتم العثور على سعر لـ {symbol}")
994
  return None
995
 
996
  except Exception as e:
997
- print(f"❌ خطأ في get_latest_price_async لـ {symbol}: {e}")
998
  return None
999
 
1000
  async def get_available_symbols(self):
 
1001
  try:
1002
  if not self.exchange:
1003
  return []
@@ -1013,10 +495,11 @@ class DataManager:
1013
  return usdt_symbols
1014
 
1015
  except Exception as e:
1016
- print(f"❌ خطأ في get_available_symbols: {e}")
1017
  return []
1018
 
1019
  async def validate_symbol(self, symbol):
 
1020
  try:
1021
  if not self.exchange:
1022
  return False
@@ -1027,7 +510,7 @@ class DataManager:
1027
  return symbol in self.market_cache and self.market_cache[symbol].get('active', False)
1028
 
1029
  except Exception as e:
1030
- print(f"❌ خطأ في validate_symbol لـ {symbol}: {e}")
1031
  return False
1032
 
1033
- print("✅ DataManager loaded - Real-time market data from KuCoin ready (General Whale Monitoring DISABLED)")
 
8
  import ccxt.pro as ccxt
9
  import numpy as np
10
  import logging
11
+ from typing import List, Dict, Any
12
 
 
13
  logging.getLogger("httpx").setLevel(logging.WARNING)
14
  logging.getLogger("httpcore").setLevel(logging.WARNING)
15
 
 
31
  print(f"❌ فشل تهيئة اتصال KuCoin: {e}")
32
  self.exchange = None
33
 
 
34
  self.http_client = None
 
 
35
  self.market_cache = {}
36
  self.last_market_load = None
37
 
 
 
 
 
 
38
  async def initialize(self):
39
  self.http_client = httpx.AsyncClient(timeout=20.0)
 
 
 
 
 
 
 
 
 
 
 
40
  await self._load_markets()
41
+ print("✅ DataManager initialized - Focused on data retrieval only")
42
+
43
  async def _load_markets(self):
44
  try:
45
  if not self.exchange:
 
60
  if self.exchange:
61
  await self.exchange.close()
62
 
63
+ async def get_market_context_async(self):
64
+ """جلب سياق السوق الأساسي فقط"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  try:
66
+ sentiment_data = await self.get_sentiment_safe_async()
67
+ price_data = await self._get_prices_with_fallback()
68
+
69
+ bitcoin_price = price_data.get('bitcoin')
70
+ ethereum_price = price_data.get('ethereum')
71
+
72
+ market_context = {
73
+ 'timestamp': datetime.now().isoformat(),
74
+ 'bitcoin_price_usd': bitcoin_price,
75
+ 'ethereum_price_usd': ethereum_price,
76
+ 'fear_and_greed_index': sentiment_data.get('feargreed_value') if sentiment_data else None,
77
+ 'sentiment_class': sentiment_data.get('feargreed_class') if sentiment_data else 'NEUTRAL',
78
+ 'market_trend': self._determine_market_trend(bitcoin_price, sentiment_data),
79
+ 'btc_sentiment': self._get_btc_sentiment(bitcoin_price),
80
+ 'data_quality': 'HIGH' if bitcoin_price and ethereum_price else 'LOW'
81
+ }
82
 
83
+ return market_context
 
 
84
 
 
 
 
 
 
 
 
 
85
  except Exception as e:
86
+ print(f"❌ فشل جلب سياق السوق: {e}")
87
+ return self._get_minimal_market_context()
 
 
 
 
 
 
 
 
 
 
 
88
 
89
+ async def get_sentiment_safe_async(self):
90
+ """جلب بيانات المشاعر"""
91
  try:
92
+ async with httpx.AsyncClient(timeout=8) as client:
93
+ response = await client.get("https://api.alternative.me/fng/")
 
 
94
  response.raise_for_status()
95
  data = response.json()
96
 
97
+ if 'data' not in data or not data['data']:
98
+ raise ValueError("بيانات المشاعر غير متوفرة")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ latest_data = data['data'][0]
101
+ return {
102
+ "feargreed_value": int(latest_data['value']),
103
+ "feargreed_class": latest_data['value_classification'],
104
+ "source": "alternative.me",
105
+ "timestamp": datetime.now().isoformat()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  }
107
+ except Exception as e:
108
+ print(f"❌ فشل جلب بيانات المشاعر: {e}")
109
+ return None
 
 
 
 
 
 
110
 
111
+ def _determine_market_trend(self, bitcoin_price, sentiment_data):
112
+ """تحديد اتجاه السوق"""
113
+ if bitcoin_price is None:
114
+ return "UNKNOWN"
 
 
 
 
 
 
 
 
115
 
116
+ score = 0
117
+ if bitcoin_price > 60000:
118
+ score += 1
119
+ elif bitcoin_price < 55000:
120
+ score -= 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
+ if sentiment_data and sentiment_data.get('feargreed_value') is not None:
123
+ fear_greed = sentiment_data.get('feargreed_value')
124
+ if fear_greed > 60:
125
+ score += 1
126
+ elif fear_greed < 40:
127
+ score -= 1
128
 
129
+ if score >= 1:
130
+ return "bull_market"
131
+ elif score <= -1:
132
+ return "bear_market"
133
  else:
134
+ return "sideways_market"
135
 
136
  def _get_btc_sentiment(self, bitcoin_price):
137
  if bitcoin_price is None:
 
144
  return 'NEUTRAL'
145
 
146
  async def _get_prices_with_fallback(self):
147
+ """جلب أسعار البيتكوين والإيثيريوم"""
148
  try:
149
  prices = await self._get_prices_from_kucoin_safe()
150
  if prices.get('bitcoin') and prices.get('ethereum'):
151
  return prices
152
+ return await self._get_prices_from_coingecko()
 
 
 
 
 
 
153
  except Exception as e:
154
  print(f"❌ فشل جلب الأسعار: {e}")
155
  return {'bitcoin': None, 'ethereum': None}
 
161
  try:
162
  prices = {'bitcoin': None, 'ethereum': None}
163
 
164
+ btc_ticker = await self.exchange.fetch_ticker('BTC/USDT')
165
+ btc_price = float(btc_ticker.get('last', 0)) if btc_ticker.get('last') else None
166
+ if btc_price and btc_price > 0:
167
+ prices['bitcoin'] = btc_price
 
 
 
 
 
 
 
168
 
169
+ eth_ticker = await self.exchange.fetch_ticker('ETH/USDT')
170
+ eth_price = float(eth_ticker.get('last', 0)) if eth_ticker.get('last') else None
171
+ if eth_price and eth_price > 0:
172
+ prices['ethereum'] = eth_price
 
 
 
 
 
 
 
173
 
174
  return prices
175
 
176
  except Exception as e:
177
+ print(f"❌ خطأ في جلب الأسعار من KuCoin: {e}")
178
  return {'bitcoin': None, 'ethereum': None}
179
 
180
  async def _get_prices_from_coingecko(self):
181
+ """الاحتياطي: جلب الأسعار من CoinGecko"""
182
  try:
183
  await asyncio.sleep(0.5)
184
  url = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd"
 
191
  eth_price = data.get('ethereum', {}).get('usd')
192
 
193
  if btc_price and eth_price:
 
 
 
 
194
  return {'bitcoin': btc_price, 'ethereum': eth_price}
195
  else:
 
196
  return {'bitcoin': None, 'ethereum': None}
197
 
198
  except Exception as e:
 
200
  return {'bitcoin': None, 'ethereum': None}
201
 
202
  def _get_minimal_market_context(self):
203
+ """سياق سوق بدائي عند الفشل"""
204
  return {
205
  'timestamp': datetime.now().isoformat(),
206
  'data_available': False,
 
 
207
  'market_trend': 'UNKNOWN',
208
  'btc_sentiment': 'UNKNOWN',
209
+ 'data_quality': 'LOW'
 
 
 
 
 
 
 
 
 
 
 
210
  }
211
 
212
+ async def layer1_rapid_screening(self) -> List[Dict[str, Any]]:
213
+ """
214
+ الطبقة 1: فحص سريع لجميع عملات KuCoin
215
+ يركز فقط على جلب البيانات الأساسية بدون تحليل متقدم
216
+ """
217
+ candidates = []
218
+
219
+ # الحصول على جميع الرموز النشطة
220
+ usdt_symbols = [
221
+ symbol for symbol in self.market_cache.keys()
222
+ if symbol.endswith('/USDT') and self.market_cache[symbol].get('active', False)
223
+ ]
224
+
225
+ print(f"📊 الطبقة 1: فحص سريع لـ {len(usdt_symbols)} عملة في KuCoin...")
226
+
227
+ batch_size = 50
228
+ for i in range(0, len(usdt_symbols), batch_size):
229
+ batch = usdt_symbols[i:i + batch_size]
230
+ print(f" 🔍 معالجة مجموعة {i//batch_size + 1}/{(len(usdt_symbols)//batch_size)+1}...")
231
 
232
+ batch_candidates = await self._process_batch_rapid_screening(batch)
233
+ candidates.extend(batch_candidates)
 
 
234
 
235
+ await asyncio.sleep(1)
 
 
 
 
 
 
236
 
237
+ # ترتيب المرشحين حسب قوة الأساسيات
238
+ candidates.sort(key=lambda x: x.get('layer1_score', 0), reverse=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
 
240
+ # نأخذ أفضل 80-180 مرشح للطبقة 2
241
+ target_count = min(max(80, len(candidates) // 5), 180)
242
+ final_candidates = candidates[:target_count]
 
 
 
 
 
 
 
 
243
 
244
+ print(f"✅ تم اختيار {len(final_candidates)} عملة من الطبقة 1")
245
+
246
+ # عرض أفضل 5 مرشحين
247
+ print(" 🏆 أفضل 5 مرشحين من الطبقة 1:")
248
+ for i, candidate in enumerate(final_candidates[:5]):
249
+ score = candidate.get('layer1_score', 0)
250
+ volume = candidate.get('dollar_volume', 0)
251
+ change = candidate.get('price_change_24h', 0)
252
+ print(f" {i+1}. {candidate['symbol']}: {score:.3f} | ${volume:,.0f} | {change:+.1f}%")
253
 
254
+ return final_candidates
255
 
256
+ async def _process_batch_rapid_screening(self, symbols_batch: List[str]) -> List[Dict[str, Any]]:
257
+ """معالجة دفعة من الرموز في الطبقة 1"""
258
+ candidates = []
259
+
260
+ for symbol in symbols_batch:
261
+ try:
262
+ # جلب بيانات التداول الأساسية فقط
263
+ ticker = await self.exchange.fetch_ticker(symbol)
264
+ if not ticker:
265
+ continue
266
+
267
+ current_price = ticker.get('last', 0)
268
+ volume_24h = ticker.get('baseVolume', 0)
269
+ dollar_volume = volume_24h * current_price
270
+ price_change_24h = ticker.get('percentage', 0)
271
+ high_24h = ticker.get('high', 0)
272
+ low_24h = ticker.get('low', 0)
273
+
274
+ # المعايير الأساسية المطلوبة
275
+ meets_criteria = all([
276
+ dollar_volume >= 1000000, # حجم تداول لا يقل عن 1M دولار
277
+ current_price > 0.00000001, # أي سعر مقبول
278
+ current_price <= 100000, # حد أقصى معقول
279
+ high_24h > 0, # بيانات سعر صالحة
280
+ low_24h > 0
281
+ ])
282
+
283
+ if not meets_criteria:
284
+ continue
285
+
286
+ # حساب مؤشرات أساسية بسيطة فقط
287
+ volume_strength = self._calculate_volume_strength(dollar_volume)
288
+ price_momentum = self._calculate_price_momentum(price_change_24h)
289
+ price_position = self._calculate_price_position(current_price, high_24h, low_24h)
290
+ volatility = self._calculate_volatility(high_24h, low_24h, current_price)
291
+
292
+ # الدرجة النهائية للطبقة 1 (أساسيات فقط)
293
+ layer1_score = (
294
+ volume_strength * 0.35 +
295
+ price_momentum * 0.30 +
296
+ price_position * 0.20 +
297
+ volatility * 0.15
298
+ )
299
+
300
+ if layer1_score >= 0.3:
301
+ candidate_data = {
302
+ 'symbol': symbol,
303
+ 'current_price': current_price,
304
+ 'volume_24h': volume_24h,
305
+ 'dollar_volume': dollar_volume,
306
+ 'price_change_24h': price_change_24h,
307
+ 'high_24h': high_24h,
308
+ 'low_24h': low_24h,
309
+ 'layer1_score': layer1_score,
310
+ 'volume_strength': volume_strength,
311
+ 'price_momentum': price_momentum,
312
+ 'reasons': self._generate_layer1_reasons(volume_strength, price_momentum, dollar_volume, price_change_24h)
313
+ }
314
+
315
+ candidates.append(candidate_data)
316
+
317
+ except Exception as e:
318
+ if "rate limit" not in str(e).lower():
319
+ continue
320
+
321
+ return candidates
322
+
323
+ def _calculate_volume_strength(self, dollar_volume: float) -> float:
324
+ """حساب قوة الحجم (بيانات فقط)"""
325
+ if dollar_volume >= 10000000:
326
+ return 1.0
327
+ elif dollar_volume >= 5000000:
328
+ return 0.8
329
+ elif dollar_volume >= 2000000:
330
+ return 0.6
331
+ elif dollar_volume >= 1000000:
332
+ return 0.4
333
  else:
334
+ return 0.2
335
+
336
+ def _calculate_price_momentum(self, price_change_24h: float) -> float:
337
+ """حساب زخم السعر (بيانات فقط)"""
338
+ if price_change_24h >= 15:
339
+ return 0.9
340
+ elif price_change_24h >= 8:
341
+ return 0.7
342
+ elif price_change_24h >= 3:
343
+ return 0.5
344
+ elif price_change_24h <= -15:
345
+ return 0.8
346
+ elif price_change_24h <= -8:
347
+ return 0.6
348
+ elif price_change_24h <= -3:
349
+ return 0.4
350
+ else:
351
+ return 0.3
352
 
353
+ def _calculate_price_position(self, current_price: float, high_24h: float, low_24h: float) -> float:
354
+ """حساب موقع السعر (بيانات فقط)"""
355
+ if high_24h == low_24h:
356
+ return 0.5
357
+
358
+ position = (current_price - low_24h) / (high_24h - low_24h)
359
+
360
+ if position < 0.3:
361
+ return 0.8
362
+ elif position > 0.7:
363
+ return 0.6
364
  else:
365
+ return 0.5
 
 
 
 
 
366
 
367
+ def _calculate_volatility(self, high_24h: float, low_24h: float, current_price: float) -> float:
368
+ """حساب التقلب (بيانات فقط)"""
369
+ if current_price == 0:
370
+ return 0.5
371
+
372
+ volatility = (high_24h - low_24h) / current_price
373
+
374
+ if volatility > 0.5:
375
+ return 0.2
376
+ elif volatility > 0.2:
377
+ return 0.4
378
+ elif volatility > 0.1:
379
+ return 0.8
380
+ elif volatility > 0.05:
381
+ return 0.6
382
+ else:
383
+ return 0.3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
 
385
+ def _generate_layer1_reasons(self, volume_strength: float, price_momentum: float,
386
+ dollar_volume: float, price_change: float) -> List[str]:
387
+ """توليد أسباب الترشيح بناءً على البيانات فقط"""
388
+ reasons = []
389
+
390
+ if volume_strength >= 0.6:
391
+ reasons.append('high_liquidity')
392
+ elif volume_strength >= 0.4:
393
+ reasons.append('good_liquidity')
394
+
395
+ if price_change >= 8:
396
+ reasons.append('strong_positive_momentum')
397
+ elif price_change >= 3:
398
+ reasons.append('positive_momentum')
399
+ elif price_change <= -8:
400
+ reasons.append('oversold_opportunity')
401
+ elif price_change <= -3:
402
+ reasons.append('dip_opportunity')
403
+
404
+ if dollar_volume >= 5000000:
405
+ reasons.append('very_high_volume')
406
+ elif dollar_volume >= 2000000:
407
+ reasons.append('high_volume')
408
+
409
+ return reasons
410
+
411
+ async def get_ohlcv_data_for_symbols(self, symbols: List[str]) -> List[Dict[str, Any]]:
412
  """
413
+ جلب بيانات OHLCV كاملة للرموز المحددة
414
+ يستخدم في الطبقة 2 للتحليل المتقدم
 
 
 
415
  """
416
+ results = []
417
+
418
+ print(f"📊 جلب بيانات OHLCV لـ {len(symbols)} عملة...")
419
+
420
+ for symbol in symbols:
421
+ try:
422
+ ohlcv_data = {}
423
+ timeframes = [
424
+ ('5m', 100), ('15m', 100), ('1h', 100),
425
+ ('4h', 100), ('1d', 100), ('1w', 50)
426
+ ]
427
+
428
+ has_sufficient_data = True
429
+ for timeframe, limit in timeframes:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  try:
431
+ ohlcv = await self.exchange.fetch_ohlcv(symbol, timeframe, limit=limit)
432
+ if ohlcv and len(ohlcv) >= 20:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  ohlcv_data[timeframe] = ohlcv
434
+ else:
 
 
 
 
 
435
  has_sufficient_data = False
436
  break
437
+ except Exception:
438
+ has_sufficient_data = False
439
+ break
440
+
441
+ if has_sufficient_data:
442
+ # الحصول على بيانات التداول الحالية
443
+ ticker = await self.exchange.fetch_ticker(symbol)
444
+ current_price = ticker.get('last', 0) if ticker else 0
445
 
446
+ result_data = {
447
+ 'symbol': symbol,
448
+ 'ohlcv': ohlcv_data,
449
+ 'current_price': current_price,
450
+ 'timestamp': datetime.now().isoformat()
451
+ }
452
+ results.append(result_data)
453
+
454
+ await asyncio.sleep(0.1)
455
+
456
+ except Exception as symbol_error:
457
+ print(f"❌ خطأ في جلب بيانات {symbol}: {symbol_error}")
458
+ continue
459
+
460
+ print(f" تم تجميع بيانات OHLCV لـ {len(results)} عملة")
461
+ return results
 
 
 
 
 
 
 
462
 
463
  async def get_latest_price_async(self, symbol):
464
+ """جلب السعر الحالي لعملة محددة"""
465
  try:
466
  if not self.exchange:
 
 
 
 
 
467
  return None
468
 
469
  ticker = await self.exchange.fetch_ticker(symbol)
 
472
  if current_price:
473
  return float(current_price)
474
  else:
 
475
  return None
476
 
477
  except Exception as e:
478
+ print(f"❌ خطأ في جلب السعر لـ {symbol}: {e}")
479
  return None
480
 
481
  async def get_available_symbols(self):
482
+ """الحصول على جميع الرموز المتاحة"""
483
  try:
484
  if not self.exchange:
485
  return []
 
495
  return usdt_symbols
496
 
497
  except Exception as e:
498
+ print(f"❌ خطأ في جلب الرموز المتاحة: {e}")
499
  return []
500
 
501
  async def validate_symbol(self, symbol):
502
+ """التحقق من صحة الرمز"""
503
  try:
504
  if not self.exchange:
505
  return False
 
510
  return symbol in self.market_cache and self.market_cache[symbol].get('active', False)
511
 
512
  except Exception as e:
513
+ print(f"❌ خطأ في التحقق من الرمز {symbol}: {e}")
514
  return False
515
 
516
+ print("✅ DataManager loaded - Focused on data retrieval only")