Riy777 commited on
Commit
af73eb9
·
1 Parent(s): 248e033

Update data_manager.py

Browse files
Files changed (1) hide show
  1. data_manager.py +232 -216
data_manager.py CHANGED
@@ -5,7 +5,7 @@ import httpx
5
  import traceback
6
  import time
7
  from datetime import datetime
8
- import ccxt.pro as ccxt
9
  import numpy as np
10
  import logging
11
  from typing import List, Dict, Any
@@ -19,18 +19,17 @@ class DataManager:
19
  self.whale_monitor = whale_monitor
20
 
21
  # إعدادات الأداء المحسنة
22
- self.batch_size = 50 # تخفيض حجم الدفعة لتجنب rate limits
23
  self.cache_duration = 300
24
 
25
  try:
26
  self.exchange = ccxt.kucoin({
27
  'sandbox': False,
28
  'enableRateLimit': True,
29
- 'timeout': 30000, # زيادة المهلة
30
  'verbose': False,
31
- 'rateLimit': 1000, # زيادة معدل الطلبات
32
  })
33
- print("✅ تم تهيئة اتصال KuCoin بنجاح")
34
  except Exception as e:
35
  print(f"❌ فشل تهيئة اتصال KuCoin: {e}")
36
  self.exchange = None
@@ -44,7 +43,7 @@ class DataManager:
44
  async def initialize(self):
45
  self.http_client = httpx.AsyncClient(timeout=30.0)
46
  await self._load_markets()
47
- print("✅ DataManager initialized - Optimized for OHLCV data retrieval")
48
 
49
  async def _load_markets(self):
50
  try:
@@ -52,7 +51,7 @@ class DataManager:
52
  return
53
 
54
  print("🔄 جلب أحدث بيانات الأسواق من KuCoin...")
55
- await self.exchange.load_markets()
56
  self.market_cache = self.exchange.markets
57
  self.last_market_load = datetime.now()
58
  print(f"✅ تم تحميل {len(self.market_cache)} سوق من KuCoin")
@@ -63,8 +62,7 @@ class DataManager:
63
  async def close(self):
64
  if self.http_client:
65
  await self.http_client.aclose()
66
- if self.exchange:
67
- await self.exchange.close()
68
 
69
  async def get_market_context_async(self):
70
  """جلب سياق السوق الأساسي فقط"""
@@ -167,12 +165,12 @@ class DataManager:
167
  try:
168
  prices = {'bitcoin': None, 'ethereum': None}
169
 
170
- btc_ticker = await self.exchange.fetch_ticker('BTC/USDT')
171
  btc_price = float(btc_ticker.get('last', 0)) if btc_ticker.get('last') else None
172
  if btc_price and btc_price > 0:
173
  prices['bitcoin'] = btc_price
174
 
175
- eth_ticker = await self.exchange.fetch_ticker('ETH/USDT')
176
  eth_price = float(eth_ticker.get('last', 0)) if eth_ticker.get('last') else None
177
  if eth_price and eth_price > 0:
178
  prices['ethereum'] = eth_price
@@ -217,10 +215,9 @@ class DataManager:
217
 
218
  async def layer1_rapid_screening(self) -> List[Dict[str, Any]]:
219
  """
220
- الطبقة 1: فحص سريع لجميع عملات KuCoin
221
- يركز فقط على جلب البيانات الأساسية بدون تحليل متقدم
222
  """
223
- candidates = []
224
 
225
  # الحصول على جميع الرموز النشطة
226
  usdt_symbols = [
@@ -228,265 +225,284 @@ class DataManager:
228
  if symbol.endswith('/USDT') and self.market_cache[symbol].get('active', False)
229
  ]
230
 
231
- print(f"📊 الطبقة 1: فحص سريع لـ {len(usdt_symbols)} عملة في KuCoin...")
232
 
233
- batch_size = 20 # تخفيض حجم الدفعة لتجنب rate limits
234
- for i in range(0, len(usdt_symbols), batch_size):
235
- batch = usdt_symbols[i:i + batch_size]
236
- print(f" 🔍 معالجة مجموعة {i//batch_size + 1}/{(len(usdt_symbols)//batch_size)+1}...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
 
238
- batch_candidates = await self._process_batch_rapid_screening(batch)
239
- candidates.extend(batch_candidates)
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
- await asyncio.sleep(1) # زيادة وقت الانتظار بين الدفعات
 
 
 
 
242
 
243
- # ترتيب المرشحين حسب قوة الأساسيات
244
- candidates.sort(key=lambda x: x.get('layer1_score', 0), reverse=True)
 
 
 
245
 
246
- # نأخذ أفضل 50-100 مرشح للطبقة 2
247
- target_count = min(max(50, len(candidates) // 3), 100, len(candidates))
248
- final_candidates = candidates[:target_count]
249
 
250
- print(f"✅ تم اختيار {len(final_candidates)} عملة من الطبقة 1")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
- # عرض أفضل 10 مرشحين
253
- print(" 🏆 أفضل 10 مرشحين من الطبقة 1:")
254
- for i, candidate in enumerate(final_candidates[:10]):
255
- score = candidate.get('layer1_score', 0)
256
- volume = candidate.get('dollar_volume', 0)
257
- change = candidate.get('price_change_24h', 0)
258
- print(f" {i+1}. {candidate['symbol']}: {score:.3f} | ${volume:,.0f} | {change:+.1f}%")
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
- return final_candidates
261
-
262
- async def _process_batch_rapid_screening(self, symbols_batch: List[str]) -> List[Dict[str, Any]]:
263
- """معالجة دفعة من الرموز في الطبقة 1"""
264
- candidates = []
 
 
 
 
 
265
 
266
- for symbol in symbols_batch:
267
- try:
268
- # جلب بيانات التداول الأساسية فقط
269
- ticker = await self.exchange.fetch_ticker(symbol)
270
- if not ticker:
271
- continue
272
-
273
- current_price = ticker.get('last', 0)
274
- volume_24h = ticker.get('baseVolume', 0)
275
- dollar_volume = volume_24h * current_price
276
- price_change_24h = ticker.get('percentage', 0)
277
- high_24h = ticker.get('high', 0)
278
- low_24h = ticker.get('low', 0)
279
-
280
- # المعايير الأساسية المطلوبة - تخفيف الشروط
281
- meets_criteria = all([
282
- dollar_volume >= 500000, # تخفيف من 1M إلى 500K دولار
283
- current_price > 0.00000001, # أي سعر مقبول
284
- current_price <= 100000, # حد أقصى معقول
285
- high_24h > 0, # بيانات سعر صالحة
286
- low_24h > 0
287
- ])
288
-
289
- if not meets_criteria:
290
- continue
291
-
292
- # حساب مؤشرات أساسية بسيطة فقط
293
- volume_strength = self._calculate_volume_strength(dollar_volume)
294
- price_momentum = self._calculate_price_momentum(price_change_24h)
295
- price_position = self._calculate_price_position(current_price, high_24h, low_24h)
296
- volatility = self._calculate_volatility(high_24h, low_24h, current_price)
297
-
298
- # الدرجة النهائية للطبقة 1 (أساسيات فقط)
299
- layer1_score = (
300
- volume_strength * 0.35 +
301
- price_momentum * 0.30 +
302
- price_position * 0.20 +
303
- volatility * 0.15
304
- )
305
-
306
- if layer1_score >= 0.2: # تخفيف من 0.3 إلى 0.2
307
- candidate_data = {
308
- 'symbol': symbol,
309
- 'current_price': current_price,
310
- 'volume_24h': volume_24h,
311
- 'dollar_volume': dollar_volume,
312
- 'price_change_24h': price_change_24h,
313
- 'high_24h': high_24h,
314
- 'low_24h': low_24h,
315
- 'layer1_score': layer1_score,
316
- 'volume_strength': volume_strength,
317
- 'price_momentum': price_momentum,
318
- 'reasons': self._generate_layer1_reasons(volume_strength, price_momentum, dollar_volume, price_change_24h)
319
- }
320
-
321
- candidates.append(candidate_data)
322
-
323
- except Exception as e:
324
- if "rate limit" not in str(e).lower():
325
- continue
326
- await asyncio.sleep(2) # انتظار في حالة rate limit
327
 
328
- return candidates
 
 
 
 
 
 
329
 
330
- def _calculate_volume_strength(self, dollar_volume: float) -> float:
331
- """حساب قوة الحجم (بيانات فقط)"""
332
- if dollar_volume >= 5000000: # تخفيف من 10M إلى 5M
333
  return 1.0
334
- elif dollar_volume >= 2000000: # تخفيف من 5M إلى 2M
335
  return 0.8
336
- elif dollar_volume >= 1000000: # تخفيف من 2M إلى 1M
337
  return 0.6
338
- elif dollar_volume >= 500000: # تخفيف من 1M إلى 500K
339
  return 0.4
340
- else:
341
  return 0.2
342
-
343
- def _calculate_price_momentum(self, price_change_24h: float) -> float:
344
- """حساب زخم السعر (بيانات فقط)"""
345
- if price_change_24h >= 10: # تخفيف من 15 إلى 10
346
- return 0.9
347
- elif price_change_24h >= 5: # تخفيف من 8 إلى 5
348
- return 0.7
349
- elif price_change_24h >= 2: # تخفيف من 3 إلى 2
350
- return 0.5
351
- elif price_change_24h <= -10: # تخفيف من -15 إلى -10
352
- return 0.8
353
- elif price_change_24h <= -5: # تخفيف من -8 إلى -5
354
- return 0.6
355
- elif price_change_24h <= -2: # تخفيف من -3 إلى -2
356
- return 0.4
357
  else:
358
- return 0.3
359
 
360
- def _calculate_price_position(self, current_price: float, high_24h: float, low_24h: float) -> float:
361
- """حساب موقع السعر (بيانات فقط)"""
362
- if high_24h == low_24h:
363
  return 0.5
364
 
365
- position = (current_price - low_24h) / (high_24h - low_24h)
 
 
366
 
367
- if position < 0.2: # تخفيف من 0.3 إلى 0.2
368
- return 0.8
369
- elif position > 0.8: # تخفيف من 0.7 إلى 0.8
370
- return 0.6
371
- else:
372
- return 0.5
373
-
374
- def _calculate_volatility(self, high_24h: float, low_24h: float, current_price: float) -> float:
375
- """حساب التقلب (بيانات فقط)"""
376
- if current_price == 0:
377
- return 0.5
378
-
379
- volatility = (high_24h - low_24h) / current_price
380
-
381
- if volatility > 0.3: # تخفيف من 0.5 إلى 0.3
382
- return 0.2
383
- elif volatility > 0.15: # تخفيف من 0.2 إلى 0.15
384
- return 0.4
385
- elif volatility > 0.08: # تخفيف من 0.1 إلى 0.08
386
- return 0.8
387
- elif volatility > 0.04: # تخفيف من 0.05 إلى 0.04
388
- return 0.6
389
- else:
390
- return 0.3
391
-
392
- def _generate_layer1_reasons(self, volume_strength: float, price_momentum: float,
393
- dollar_volume: float, price_change: float) -> List[str]:
394
- """توليد أسباب الترشيح بناءً على البيانات فقط"""
395
- reasons = []
396
-
397
- if volume_strength >= 0.6:
398
- reasons.append('high_liquidity')
399
- elif volume_strength >= 0.4:
400
- reasons.append('good_liquidity')
401
-
402
- if price_change >= 5: # تخفيف من 8 إلى 5
403
- reasons.append('strong_positive_momentum')
404
- elif price_change >= 2: # تخفيف من 3 إلى 2
405
- reasons.append('positive_momentum')
406
- elif price_change <= -5: # تخفيف من -8 إلى -5
407
- reasons.append('oversold_opportunity')
408
- elif price_change <= -2: # تخفيف من -3 إلى -2
409
- reasons.append('dip_opportunity')
410
-
411
- if dollar_volume >= 2000000: # تخفيف من 5M إلى 2M
412
- reasons.append('very_high_volume')
413
- elif dollar_volume >= 1000000: # تخفيف من 2M إلى 1M
414
- reasons.append('high_volume')
415
-
416
- return reasons
417
 
418
  async def get_ohlcv_data_for_symbols(self, symbols: List[str]) -> List[Dict[str, Any]]:
419
  """
420
- جلب بيانات OHLCV كاملة للرموز المحددة
421
- يستخدم في الطبقة 2 للتحليل المتقدم
422
  """
423
  results = []
424
 
425
- print(f"📊 جلب بيانات OHLCV لـ {len(symbols)} عملة...")
 
426
 
427
- for symbol in symbols:
428
  try:
429
- ohlcv_data = await self._fetch_ohlcv_for_symbol_safe(symbol)
430
  if ohlcv_data:
431
  results.append(ohlcv_data)
432
- print(f" ✅ تم جلب بيانات {symbol}")
433
  else:
434
- print(f" ❌ فشل جلب بيانات {symbol}")
435
 
436
- await asyncio.sleep(0.5) # زيادة وقت الانتظار بين الطلبات
 
437
 
438
  except Exception as symbol_error:
439
- print(f"❌ خطأ في جلب بيانات {symbol}: {symbol_error}")
440
  continue
441
 
442
- print(f"✅ تم تجميع بيانات OHLCV لـ {len(results)} عملة")
443
  return results
444
 
445
- async def _fetch_ohlcv_for_symbol_safe(self, symbol: str) -> Dict[str, Any]:
446
- """جلب بيانات OHLCV لرمز واحد بشكل آمن"""
447
  try:
448
  ohlcv_data = {}
449
 
450
- # استخدام إطارات زمنية أساسية فقط لتقليل الطلبات
451
  timeframes = [
452
- ('1h', 50), # ساعة واحدة - أساسي
453
- ('4h', 25), # 4 ساعات
454
- ('1d', 30), # يومي
 
 
 
455
  ]
456
 
457
  has_sufficient_data = True
458
 
459
  for timeframe, limit in timeframes:
460
  try:
461
- # زيادة المهلة وجعل الطلبات أكثر موثوقية
462
- ohlcv = await asyncio.wait_for(
463
- self.exchange.fetch_ohlcv(symbol, timeframe, limit=limit),
464
- timeout=15 # زيادة المهلة
465
- )
466
 
467
- if ohlcv and len(ohlcv) >= 10: # تخفيف الحد الأدنى
468
  ohlcv_data[timeframe] = ohlcv
469
  else:
470
- print(f" ⚠️ بيانات غير كافية لـ {symbol} على الإطار {timeframe}")
471
  has_sufficient_data = False
472
  break
473
 
474
- except asyncio.TimeoutError:
475
- print(f" ⏰ مهلة جلب بيانات {symbol} على الإطار {timeframe}")
476
- has_sufficient_data = False
477
- break
478
  except Exception as e:
479
- print(f" ⚠️ خطأ في جلب بيانات {symbol} على الإطار {timeframe}: {e}")
480
  has_sufficient_data = False
481
  break
482
 
483
  if has_sufficient_data and ohlcv_data:
484
- # الحصول على بيانات التداول الحالية
485
  try:
486
- ticker = await asyncio.wait_for(
487
- self.exchange.fetch_ticker(symbol),
488
- timeout=10
489
- )
490
  current_price = ticker.get('last', 0) if ticker else 0
491
 
492
  return {
@@ -496,13 +512,13 @@ class DataManager:
496
  'timestamp': datetime.now().isoformat()
497
  }
498
  except Exception as price_error:
499
- print(f" ⚠️ خطأ في جلب السعر لـ {symbol}: {price_error}")
500
  return None
501
  else:
502
  return None
503
 
504
  except Exception as e:
505
- print(f" ❌ خطأ عام في جلب بيانات {symbol}: {e}")
506
  return None
507
 
508
  async def get_latest_price_async(self, symbol):
@@ -511,7 +527,7 @@ class DataManager:
511
  if not self.exchange:
512
  return None
513
 
514
- ticker = await self.exchange.fetch_ticker(symbol)
515
  current_price = ticker.get('last')
516
 
517
  if current_price:
@@ -558,4 +574,4 @@ class DataManager:
558
  print(f"❌ خطأ في التحقق من الرمز {symbol}: {e}")
559
  return False
560
 
561
- print("✅ DataManager loaded - Optimized for OHLCV data retrieval")
 
5
  import traceback
6
  import time
7
  from datetime import datetime
8
+ import ccxt # استخدام ccxt العادي بدلاً من pro
9
  import numpy as np
10
  import logging
11
  from typing import List, Dict, Any
 
19
  self.whale_monitor = whale_monitor
20
 
21
  # إعدادات الأداء المحسنة
22
+ self.batch_size = 25 # حجم دفعة معقول
23
  self.cache_duration = 300
24
 
25
  try:
26
  self.exchange = ccxt.kucoin({
27
  'sandbox': False,
28
  'enableRateLimit': True,
29
+ 'timeout': 30000,
30
  'verbose': False,
 
31
  })
32
+ print("✅ تم تهيئة اتصال KuCoin بنجاح (CCXT Standard)")
33
  except Exception as e:
34
  print(f"❌ فشل تهيئة اتصال KuCoin: {e}")
35
  self.exchange = None
 
43
  async def initialize(self):
44
  self.http_client = httpx.AsyncClient(timeout=30.0)
45
  await self._load_markets()
46
+ print("✅ DataManager initialized - Focused on Top 200 Symbols")
47
 
48
  async def _load_markets(self):
49
  try:
 
51
  return
52
 
53
  print("🔄 جلب أحدث بيانات الأسواق من KuCoin...")
54
+ self.exchange.load_markets() # sync call instead of await
55
  self.market_cache = self.exchange.markets
56
  self.last_market_load = datetime.now()
57
  print(f"✅ تم تحميل {len(self.market_cache)} سوق من KuCoin")
 
62
  async def close(self):
63
  if self.http_client:
64
  await self.http_client.aclose()
65
+ # لا داعي لـ close في ccxt العادي
 
66
 
67
  async def get_market_context_async(self):
68
  """جلب سياق السوق الأساسي فقط"""
 
165
  try:
166
  prices = {'bitcoin': None, 'ethereum': None}
167
 
168
+ btc_ticker = self.exchange.fetch_ticker('BTC/USDT') # sync call
169
  btc_price = float(btc_ticker.get('last', 0)) if btc_ticker.get('last') else None
170
  if btc_price and btc_price > 0:
171
  prices['bitcoin'] = btc_price
172
 
173
+ eth_ticker = self.exchange.fetch_ticker('ETH/USDT') # sync call
174
  eth_price = float(eth_ticker.get('last', 0)) if eth_ticker.get('last') else None
175
  if eth_price and eth_price > 0:
176
  prices['ethereum'] = eth_price
 
215
 
216
  async def layer1_rapid_screening(self) -> List[Dict[str, Any]]:
217
  """
218
+ الطبقة 1: فحص سريع لأفضل 200 عملة بناءً على مؤشرات هامة
 
219
  """
220
+ print("📊 الطبقة 1: فحص سريع لأفضل العملات بناءً على مؤشرات هامة...")
221
 
222
  # الحصول على جميع الرموز النشطة
223
  usdt_symbols = [
 
225
  if symbol.endswith('/USDT') and self.market_cache[symbol].get('active', False)
226
  ]
227
 
228
+ print(f"🔍 تحليل {len(usdt_symbols)} عملة متاحة...")
229
 
230
+ # جلب بيانات التداول لجميع الرموز وتقييمها
231
+ all_symbols_data = []
232
+
233
+ for i, symbol in enumerate(usdt_symbols):
234
+ try:
235
+ symbol_data = await self._get_symbol_trading_data(symbol)
236
+ if symbol_data:
237
+ all_symbols_data.append(symbol_data)
238
+
239
+ if i % 50 == 0:
240
+ print(f" 📈 معالجة {i}/{len(usdt_symbols)} عملة...")
241
+
242
+ await asyncio.sleep(0.05) # وقت انتظار قصير جداً
243
+
244
+ except Exception as e:
245
+ continue
246
+
247
+ print(f"✅ تم جمع بيانات {len(all_symbols_data)} عملة")
248
+
249
+ if not all_symbols_data:
250
+ return []
251
+
252
+ # تصنيف العملات بناءً على مؤشرات هامة
253
+ scored_symbols = []
254
+
255
+ for symbol_data in all_symbols_data:
256
+ try:
257
+ score = self._calculate_comprehensive_score(symbol_data)
258
+ if score > 0: # قبول العملات ذات الدرجة الإيجابية فقط
259
+ symbol_data['layer1_score'] = score
260
+ scored_symbols.append(symbol_data)
261
+ except Exception as e:
262
+ continue
263
+
264
+ # ترتيب العملات حسب الدرجة (من الأعلى للأدنى)
265
+ scored_symbols.sort(key=lambda x: x.get('layer1_score', 0), reverse=True)
266
+
267
+ # أخذ أفضل 200 عملة فقط
268
+ top_200 = scored_symbols[:200]
269
+
270
+ print(f"🎯 تم اختيار أفضل {len(top_200)} عملة للطبقة 2")
271
+
272
+ # عرض أفضل 15 عملة
273
+ print("🏆 أفضل 15 عملة من الطبقة 1:")
274
+ for i, symbol_data in enumerate(top_200[:15]):
275
+ score = symbol_data.get('layer1_score', 0)
276
+ volume = symbol_data.get('dollar_volume', 0)
277
+ change = symbol_data.get('price_change_24h', 0)
278
+ volatility = symbol_data.get('volatility_score', 0)
279
+ print(f" {i+1:2d}. {symbol_data['symbol']}: {score:.3f} | ${volume:>8,.0f} | {change:>+6.1f}% | تقلب: {volatility:.3f}")
280
+
281
+ return top_200
282
+
283
+ async def _get_symbol_trading_data(self, symbol: str) -> Dict[str, Any]:
284
+ """جلب بيانات التداول لرمز واحد"""
285
+ try:
286
+ ticker = self.exchange.fetch_ticker(symbol)
287
+ if not ticker:
288
+ return None
289
+
290
+ current_price = ticker.get('last', 0)
291
+ volume_24h = ticker.get('baseVolume', 0)
292
+ dollar_volume = volume_24h * current_price
293
+ price_change_24h = ticker.get('percentage', 0) or 0
294
+ high_24h = ticker.get('high', 0)
295
+ low_24h = ticker.get('low', 0)
296
+ open_price = ticker.get('open', 0)
297
+
298
+ # حساب مؤشرات إضافية هامة
299
+ volatility = self._calculate_volatility_score(high_24h, low_24h, current_price)
300
+ volume_trend = self._calculate_volume_trend(dollar_volume)
301
+ price_strength = self._calculate_price_strength(current_price, open_price, price_change_24h)
302
 
303
+ return {
304
+ 'symbol': symbol,
305
+ 'current_price': current_price,
306
+ 'volume_24h': volume_24h,
307
+ 'dollar_volume': dollar_volume,
308
+ 'price_change_24h': price_change_24h,
309
+ 'high_24h': high_24h,
310
+ 'low_24h': low_24h,
311
+ 'open_price': open_price,
312
+ 'volatility_score': volatility,
313
+ 'volume_trend': volume_trend,
314
+ 'price_strength': price_strength,
315
+ 'reasons': []
316
+ }
317
 
318
+ except Exception as e:
319
+ return None
320
+
321
+ def _calculate_comprehensive_score(self, symbol_data: Dict[str, Any]) -> float:
322
+ """حساب درجة شاملة تعتمد على مؤشرات هامة للربحية"""
323
 
324
+ dollar_volume = symbol_data.get('dollar_volume', 0)
325
+ price_change = symbol_data.get('price_change_24h', 0)
326
+ volatility = symbol_data.get('volatility_score', 0)
327
+ volume_trend = symbol_data.get('volume_trend', 0)
328
+ price_strength = symbol_data.get('price_strength', 0)
329
 
330
+ # 1. السيولة والحجم (35%) - الأهم لتجنب العملات الميتة
331
+ if dollar_volume < 100000: # أقل من 100K دولار - رفض
332
+ return 0
333
 
334
+ volume_score = 0
335
+ if dollar_volume >= 10000000: # 10M+
336
+ volume_score = 1.0
337
+ elif dollar_volume >= 5000000: # 5M+
338
+ volume_score = 0.9
339
+ elif dollar_volume >= 2000000: # 2M+
340
+ volume_score = 0.8
341
+ elif dollar_volume >= 1000000: # 1M+
342
+ volume_score = 0.7
343
+ elif dollar_volume >= 500000: # 500K+
344
+ volume_score = 0.6
345
+ elif dollar_volume >= 250000: # 250K+
346
+ volume_score = 0.5
347
+ elif dollar_volume >= 100000: # 100K+
348
+ volume_score = 0.4
349
 
350
+ # 2. الزخم السعري (25%) - البحث عن حركة قوية
351
+ momentum_score = 0
352
+ if price_change >= 20: # +20%+ - قوي جداً
353
+ momentum_score = 1.0
354
+ elif price_change >= 15: # +15%+
355
+ momentum_score = 0.9
356
+ elif price_change >= 10: # +10%+
357
+ momentum_score = 0.8
358
+ elif price_change >= 5: # +5%+
359
+ momentum_score = 0.7
360
+ elif price_change >= 2: # +2%+
361
+ momentum_score = 0.6
362
+ elif price_change >= 0: # موجب
363
+ momentum_score = 0.5
364
+ elif price_change >= -5: # حتى -5% (فرصة شراء)
365
+ momentum_score = 0.4
366
+ elif price_change >= -10: # حتى -10%
367
+ momentum_score = 0.3
368
+ else: # أكثر من -10% - تجنب
369
+ momentum_score = 0.1
370
 
371
+ # 3. التقلب المناسب (20%) - ليس عالي جداً ولا منخفض جداً
372
+ volatility_score = 0
373
+ if 0.02 <= volatility <= 0.15: # تقلب مثالي 2%-15%
374
+ volatility_score = 1.0
375
+ elif 0.01 <= volatility <= 0.20: # مقبول 1%-20%
376
+ volatility_score = 0.8
377
+ elif volatility <= 0.01: # قليل جداً
378
+ volatility_score = 0.3
379
+ elif volatility > 0.20: # عالي جداً
380
+ volatility_score = 0.2
381
 
382
+ # 4. قوة السعر (20%) - مزيج من الاتجاه والقوة
383
+ strength_score = price_strength
384
+
385
+ # الدرجة النهائية
386
+ final_score = (
387
+ volume_score * 0.35 +
388
+ momentum_score * 0.25 +
389
+ volatility_score * 0.20 +
390
+ strength_score * 0.20
391
+ )
392
+
393
+ # تحديث أسباب الترشيح
394
+ reasons = []
395
+ if volume_score >= 0.7:
396
+ reasons.append('high_liquidity')
397
+ if momentum_score >= 0.7:
398
+ reasons.append('strong_momentum')
399
+ if volatility_score >= 0.8:
400
+ reasons.append('optimal_volatility')
401
+ if strength_score >= 0.7:
402
+ reasons.append('price_strength')
403
+
404
+ symbol_data['reasons'] = reasons
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
 
406
+ return final_score
407
+
408
+ def _calculate_volatility_score(self, high_24h: float, low_24h: float, current_price: float) -> float:
409
+ """حساب درجة التقلب"""
410
+ if current_price == 0:
411
+ return 0
412
+ return (high_24h - low_24h) / current_price
413
 
414
+ def _calculate_volume_trend(self, dollar_volume: float) -> float:
415
+ """حساب اتجاه الحجم"""
416
+ if dollar_volume >= 10000000:
417
  return 1.0
418
+ elif dollar_volume >= 5000000:
419
  return 0.8
420
+ elif dollar_volume >= 1000000:
421
  return 0.6
422
+ elif dollar_volume >= 500000:
423
  return 0.4
424
+ elif dollar_volume >= 100000:
425
  return 0.2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
  else:
427
+ return 0.1
428
 
429
+ def _calculate_price_strength(self, current_price: float, open_price: float, price_change: float) -> float:
430
+ """حساب قوة السعر"""
431
+ if open_price == 0:
432
  return 0.5
433
 
434
+ # قوة السعر تعتمد على المسافة من سعر الافتتاح ونسبة التغير
435
+ distance_from_open = abs(current_price - open_price) / open_price
436
+ change_strength = min(abs(price_change) / 50, 1.0) # تطبيع قوة التغير
437
 
438
+ return (distance_from_open * 0.6 + change_strength * 0.4)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
 
440
  async def get_ohlcv_data_for_symbols(self, symbols: List[str]) -> List[Dict[str, Any]]:
441
  """
442
+ جلب بيانات OHLCV كاملة للرموز المحددة مع جميع الإطارات الزمنية الـ6
 
443
  """
444
  results = []
445
 
446
+ print(f"📊 جلب بيانات OHLCV كاملة لـ {len(symbols)} عملة...")
447
+ print(" 📈 الإطارات الزمنية: 5m, 15m, 1h, 4h, 1d, 1w")
448
 
449
+ for i, symbol in enumerate(symbols):
450
  try:
451
+ ohlcv_data = await self._fetch_complete_ohlcv(symbol)
452
  if ohlcv_data:
453
  results.append(ohlcv_data)
454
+ print(f" ✅ ({i+1}/{len(symbols)}) تم جلب بيانات {symbol}")
455
  else:
456
+ print(f" ❌ ({i+1}/{len(symbols)}) فشل جلب بيانات {symbol}")
457
 
458
+ # وقت انتظار لتجنب rate limits
459
+ await asyncio.sleep(0.3)
460
 
461
  except Exception as symbol_error:
462
+ print(f" ({i+1}/{len(symbols)}) خطأ في {symbol}: {symbol_error}")
463
  continue
464
 
465
+ print(f"✅ تم تجميع بيانات OHLCV كاملة لـ {len(results)} عملة")
466
  return results
467
 
468
+ async def _fetch_complete_ohlcv(self, symbol: str) -> Dict[str, Any]:
469
+ """جلب بيانات OHLCV كاملة مع جميع الإطارات الزمنية الـ6"""
470
  try:
471
  ohlcv_data = {}
472
 
473
+ # جميع الإطارات الزمنية الـ6 المطلوبة
474
  timeframes = [
475
+ ('5m', 100), # 5 دقائق - 100 شمعة
476
+ ('15m', 100), # 15 دقيقة - 100 شمعة
477
+ ('1h', 100), # 1 ساعة - 100 شمعة
478
+ ('4h', 100), # 4 ساعات - 100 شمعة
479
+ ('1d', 100), # 1 يوم - 100 شمعة
480
+ ('1w', 50), # 1 أسبوع - 50 شمعة
481
  ]
482
 
483
  has_sufficient_data = True
484
 
485
  for timeframe, limit in timeframes:
486
  try:
487
+ # استخدام fetch_ohlcv العادي (ليس pro)
488
+ ohlcv = self.exchange.fetch_ohlcv(symbol, timeframe, limit=limit)
 
 
 
489
 
490
+ if ohlcv and len(ohlcv) >= 20: # تأكد من وجود بيانات كافية
491
  ohlcv_data[timeframe] = ohlcv
492
  else:
493
+ print(f" ⚠️ بيانات غير كافية لـ {symbol} على {timeframe}")
494
  has_sufficient_data = False
495
  break
496
 
 
 
 
 
497
  except Exception as e:
498
+ print(f" ⚠️ خطأ في {symbol} على {timeframe}: {e}")
499
  has_sufficient_data = False
500
  break
501
 
502
  if has_sufficient_data and ohlcv_data:
503
+ # جلب السعر الحالي
504
  try:
505
+ ticker = self.exchange.fetch_ticker(symbol)
 
 
 
506
  current_price = ticker.get('last', 0) if ticker else 0
507
 
508
  return {
 
512
  'timestamp': datetime.now().isoformat()
513
  }
514
  except Exception as price_error:
515
+ print(f" ⚠️ خطأ في جلب السعر لـ {symbol}: {price_error}")
516
  return None
517
  else:
518
  return None
519
 
520
  except Exception as e:
521
+ print(f" ❌ خطأ عام في {symbol}: {e}")
522
  return None
523
 
524
  async def get_latest_price_async(self, symbol):
 
527
  if not self.exchange:
528
  return None
529
 
530
+ ticker = self.exchange.fetch_ticker(symbol)
531
  current_price = ticker.get('last')
532
 
533
  if current_price:
 
574
  print(f"❌ خطأ في التحقق من الرمز {symbol}: {e}")
575
  return False
576
 
577
+ print("✅ DataManager loaded - CCXT Standard with Top 200 Symbols & Full OHLCV Timeframes")