Riy777 commited on
Commit
7f28923
·
1 Parent(s): 2850975

Update data_manager.py

Browse files
Files changed (1) hide show
  1. data_manager.py +315 -210
data_manager.py CHANGED
@@ -9,6 +9,7 @@ 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)
@@ -18,14 +19,19 @@ class DataManager:
18
  self.contracts_db = contracts_db or {}
19
  self.whale_monitor = whale_monitor
20
 
 
 
 
 
 
21
  try:
22
  self.exchange = ccxt.kucoin({
23
  'sandbox': False,
24
  'enableRateLimit': True,
25
- 'timeout': 30000,
26
  'verbose': False
27
  })
28
- self.exchange.rateLimit = 800
29
  print("✅ تم تهيئة اتصال KuCoin بنجاح")
30
  except Exception as e:
31
  print(f"❌ فشل تهيئة اتصال KuCoin: {e}")
@@ -34,11 +40,13 @@ class DataManager:
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:
@@ -63,8 +71,11 @@ class DataManager:
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')
@@ -89,7 +100,7 @@ class DataManager:
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()
@@ -109,26 +120,14 @@ class DataManager:
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"
@@ -146,10 +145,31 @@ class DataManager:
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}
@@ -159,17 +179,19 @@ class DataManager:
159
  return {'bitcoin': None, 'ethereum': None}
160
 
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
 
@@ -180,10 +202,9 @@ class DataManager:
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"
185
  async with httpx.AsyncClient() as client:
186
- response = await client.get(url, timeout=10)
187
  response.raise_for_status()
188
  data = response.json()
189
 
@@ -199,6 +220,30 @@ class DataManager:
199
  print(f"❌ فشل جلب الأسعار من CoinGecko: {e}")
200
  return {'bitcoin': None, 'ethereum': None}
201
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  def _get_minimal_market_context(self):
203
  """سياق سوق بدائي عند الفشل"""
204
  return {
@@ -211,8 +256,7 @@ class DataManager:
211
 
212
  async def layer1_rapid_screening(self) -> List[Dict[str, Any]]:
213
  """
214
- الطبقة 1: فحص سريع لجميع عملات KuCoin
215
- يركز فقط على جلب البيانات الأساسية بدون تحليل متقدم
216
  """
217
  candidates = []
218
 
@@ -222,17 +266,19 @@ class DataManager:
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)
@@ -253,143 +299,152 @@ class DataManager:
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:
@@ -401,81 +456,125 @@ class DataManager:
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)
 
 
 
 
 
 
 
 
 
470
  current_price = ticker.get('last')
471
 
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):
@@ -513,4 +612,10 @@ class DataManager:
513
  print(f"❌ خطأ في التحقق من الرمز {symbol}: {e}")
514
  return False
515
 
516
- print("✅ DataManager loaded - Focused on data retrieval only")
 
 
 
 
 
 
 
9
  import numpy as np
10
  import logging
11
  from typing import List, Dict, Any
12
+ from concurrent.futures import ThreadPoolExecutor
13
 
14
  logging.getLogger("httpx").setLevel(logging.WARNING)
15
  logging.getLogger("httpcore").setLevel(logging.WARNING)
 
19
  self.contracts_db = contracts_db or {}
20
  self.whale_monitor = whale_monitor
21
 
22
+ # إعدادات الأداء المحسنة
23
+ self.batch_size = 100 # زيادة حجم الدفعة
24
+ self.max_workers = 20 # زيادة العاملين المتوازيين
25
+ self.cache_duration = 300 # تخزين النتائج لمدة 5 دقائق
26
+
27
  try:
28
  self.exchange = ccxt.kucoin({
29
  'sandbox': False,
30
  'enableRateLimit': True,
31
+ 'timeout': 15000, # تقليل المهلة
32
  'verbose': False
33
  })
34
+ self.exchange.rateLimit = 500 # زيادة معدل الطلبات
35
  print("✅ تم تهيئة اتصال KuCoin بنجاح")
36
  except Exception as e:
37
  print(f"❌ فشل تهيئة اتصال KuCoin: {e}")
 
40
  self.http_client = None
41
  self.market_cache = {}
42
  self.last_market_load = None
43
+ self.symbol_cache = {}
44
+ self.cache_timestamp = {}
45
 
46
  async def initialize(self):
47
+ self.http_client = httpx.AsyncClient(timeout=10.0) # تقليل المهلة
48
  await self._load_markets()
49
+ print("✅ DataManager initialized - Optimized for performance")
50
 
51
  async def _load_markets(self):
52
  try:
 
71
  async def get_market_context_async(self):
72
  """جلب سياق السوق الأساسي فقط"""
73
  try:
74
+ # استخدام المهام المتوازية
75
+ sentiment_task = asyncio.create_task(self.get_sentiment_safe_async())
76
+ price_task = asyncio.create_task(self._get_prices_with_fallback())
77
+
78
+ sentiment_data, price_data = await asyncio.gather(sentiment_task, price_task)
79
 
80
  bitcoin_price = price_data.get('bitcoin')
81
  ethereum_price = price_data.get('ethereum')
 
100
  async def get_sentiment_safe_async(self):
101
  """جلب بيانات المشاعر"""
102
  try:
103
+ async with httpx.AsyncClient(timeout=5) as client: # تقليل المهلة
104
  response = await client.get("https://api.alternative.me/fng/")
105
  response.raise_for_status()
106
  data = response.json()
 
120
  return None
121
 
122
  def _determine_market_trend(self, bitcoin_price, sentiment_data):
123
+ """تحديد اتجاه السوق (مبسط)"""
124
  if bitcoin_price is None:
125
  return "UNKNOWN"
126
 
127
+ # حساب مبسط وسريع
128
  if bitcoin_price > 60000:
 
 
 
 
 
 
 
 
 
 
 
 
129
  return "bull_market"
130
+ elif bitcoin_price < 55000:
131
  return "bear_market"
132
  else:
133
  return "sideways_market"
 
145
  async def _get_prices_with_fallback(self):
146
  """جلب أسعار البيتكوين والإيثيريوم"""
147
  try:
148
+ # محاولة KuCoin أولاً
149
  prices = await self._get_prices_from_kucoin_safe()
150
  if prices.get('bitcoin') and prices.get('ethereum'):
151
  return prices
152
+
153
+ # استخدام المهام المتوازية للبدائل
154
+ coingecko_task = asyncio.create_task(self._get_prices_from_coingecko())
155
+ binance_task = asyncio.create_task(self._get_prices_from_binance())
156
+
157
+ done, pending = await asyncio.wait(
158
+ [coingecko_task, binance_task],
159
+ timeout=3,
160
+ return_when=asyncio.FIRST_COMPLETED
161
+ )
162
+
163
+ for task in done:
164
+ result = task.result()
165
+ if result.get('bitcoin') and result.get('ethereum'):
166
+ # إلغاء المهام المعلقة
167
+ for pending_task in pending:
168
+ pending_task.cancel()
169
+ return result
170
+
171
+ return {'bitcoin': None, 'ethereum': None}
172
+
173
  except Exception as e:
174
  print(f"❌ فشل جلب الأسعار: {e}")
175
  return {'bitcoin': None, 'ethereum': None}
 
179
  return {'bitcoin': None, 'ethereum': None}
180
 
181
  try:
182
+ # استخدام المهام المتوازية لجلب الأسعار
183
+ btc_task = asyncio.create_task(self.exchange.fetch_ticker('BTC/USDT'))
184
+ eth_task = asyncio.create_task(self.exchange.fetch_ticker('ETH/USDT'))
185
+
186
+ btc_ticker, eth_ticker = await asyncio.gather(btc_task, eth_task, return_exceptions=True)
187
+
188
  prices = {'bitcoin': None, 'ethereum': None}
189
 
190
+ if not isinstance(btc_ticker, Exception) and btc_ticker.get('last'):
191
+ prices['bitcoin'] = float(btc_ticker.get('last', 0))
 
 
192
 
193
+ if not isinstance(eth_ticker, Exception) and eth_ticker.get('last'):
194
+ prices['ethereum'] = float(eth_ticker.get('last', 0))
 
 
195
 
196
  return prices
197
 
 
202
  async def _get_prices_from_coingecko(self):
203
  """الاحتياطي: جلب الأسعار من CoinGecko"""
204
  try:
 
205
  url = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd"
206
  async with httpx.AsyncClient() as client:
207
+ response = await client.get(url, timeout=5)
208
  response.raise_for_status()
209
  data = response.json()
210
 
 
220
  print(f"❌ فشل جلب الأسعار من CoinGecko: {e}")
221
  return {'bitcoin': None, 'ethereum': None}
222
 
223
+ async def _get_prices_from_binance(self):
224
+ """الاحتياطي: جلب الأسعار من Binance"""
225
+ try:
226
+ url = "https://api.binance.com/api/v3/ticker/price"
227
+ async with httpx.AsyncClient() as client:
228
+ response = await client.get(url, timeout=5)
229
+ response.raise_for_status()
230
+ data = response.json()
231
+
232
+ btc_price = None
233
+ eth_price = None
234
+
235
+ for ticker in data:
236
+ if ticker['symbol'] == 'BTCUSDT':
237
+ btc_price = float(ticker['price'])
238
+ elif ticker['symbol'] == 'ETHUSDT':
239
+ eth_price = float(ticker['price'])
240
+
241
+ return {'bitcoin': btc_price, 'ethereum': eth_price}
242
+
243
+ except Exception as e:
244
+ print(f"❌ فشل جلب الأسعار من Binance: {e}")
245
+ return {'bitcoin': None, 'ethereum': None}
246
+
247
  def _get_minimal_market_context(self):
248
  """سياق سوق بدائي عند الفشل"""
249
  return {
 
256
 
257
  async def layer1_rapid_screening(self) -> List[Dict[str, Any]]:
258
  """
259
+ الطبقة 1: فحص سريع محسن لجميع عملات KuCoin
 
260
  """
261
  candidates = []
262
 
 
266
  if symbol.endswith('/USDT') and self.market_cache[symbol].get('active', False)
267
  ]
268
 
269
+ print(f"📊 الطبقة 1: فحص سريع محسن لـ {len(usdt_symbols)} عملة في KuCoin...")
270
 
271
+ # استخدام دفعات أكبر ومعالجة متوازية
272
+ for i in range(0, len(usdt_symbols), self.batch_size):
273
+ batch = usdt_symbols[i:i + self.batch_size]
274
+ print(f" 🔍 معالجة مجموعة {i//self.batch_size + 1}/{(len(usdt_symbols)//self.batch_size)+1}...")
275
 
276
+ batch_candidates = await self._process_batch_rapid_screening_optimized(batch)
277
  candidates.extend(batch_candidates)
278
 
279
+ # تقليل وقت الانتظار بين الدفعات
280
+ if i + self.batch_size < len(usdt_symbols):
281
+ await asyncio.sleep(0.1) # تقليل وقت الانتظار
282
 
283
  # ترتيب المرشحين حسب قوة الأساسيات
284
  candidates.sort(key=lambda x: x.get('layer1_score', 0), reverse=True)
 
299
 
300
  return final_candidates
301
 
302
+ async def _process_batch_rapid_screening_optimized(self, symbols_batch: List[str]) -> List[Dict[str, Any]]:
303
+ """معالجة دفعة من الرموز في الطبقة 1 بشكل متوازي ومحسن"""
304
  candidates = []
305
 
306
+ # استخدام asyncio.gather للمعالجة المتوازية
307
+ tasks = []
308
  for symbol in symbols_batch:
309
+ # التحقق من التخزين المؤقت أولاً
310
+ cache_key = f"ticker_{symbol}"
311
+ if (cache_key in self.symbol_cache and
312
+ time.time() - self.cache_timestamp.get(cache_key, 0) < self.cache_duration):
313
+ tasks.append(asyncio.create_task(self._process_cached_symbol(symbol)))
314
+ else:
315
+ tasks.append(asyncio.create_task(self._process_symbol_optimized(symbol)))
316
+
317
+ # انتظار اكتمال جميع المهام
318
+ results = await asyncio.gather(*tasks, return_exceptions=True)
319
+
320
+ for result in results:
321
+ if isinstance(result, Exception) or result is None:
322
+ continue
323
+ candidates.append(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
 
325
  return candidates
326
 
327
+ async def _process_cached_symbol(self, symbol: str):
328
+ """معالجة الرمز من التخزين المؤقت"""
329
+ cache_key = f"ticker_{symbol}"
330
+ cached_data = self.symbol_cache.get(cache_key)
331
+ if cached_data and self._validate_cached_data(cached_data):
332
+ return cached_data
 
 
 
 
333
  else:
334
+ # إذا كانت البيانات المخزنة غير صالحة، معالجة جديدة
335
+ return await self._process_symbol_optimized(symbol)
336
 
337
+ async def _process_symbol_optimized(self, symbol: str):
338
+ """معالجة رمز واحد بشكل محسن"""
339
+ try:
340
+ # جلب بيانات التداول الأساسية فقط
341
+ ticker = await asyncio.wait_for(
342
+ self.exchange.fetch_ticker(symbol),
343
+ timeout=5 # مهلة مخفضة
344
+ )
345
+
346
+ if not ticker:
347
+ return None
348
+
349
+ current_price = ticker.get('last', 0)
350
+ volume_24h = ticker.get('baseVolume', 0)
351
+ dollar_volume = volume_24h * current_price
352
+ price_change_24h = ticker.get('percentage', 0)
353
+ high_24h = ticker.get('high', 0)
354
+ low_24h = ticker.get('low', 0)
355
+
356
+ # فحص سريع للمعايير الأساسية
357
+ if not self._quick_validation(current_price, dollar_volume, high_24h, low_24h):
358
+ return None
359
+
360
+ # حساب مؤشرات أساسية سريعة
361
+ layer1_score = self._calculate_fast_layer1_score(
362
+ dollar_volume, price_change_24h, current_price, high_24h, low_24h
363
+ )
364
+
365
+ if layer1_score >= 0.3:
366
+ candidate_data = {
367
+ 'symbol': symbol,
368
+ 'current_price': current_price,
369
+ 'volume_24h': volume_24h,
370
+ 'dollar_volume': dollar_volume,
371
+ 'price_change_24h': price_change_24h,
372
+ 'high_24h': high_24h,
373
+ 'low_24h': low_24h,
374
+ 'layer1_score': layer1_score,
375
+ 'reasons': self._generate_fast_reasons(dollar_volume, price_change_24h)
376
+ }
377
+
378
+ # تخزين في الذاكرة المؤقتة
379
+ cache_key = f"ticker_{symbol}"
380
+ self.symbol_cache[cache_key] = candidate_data
381
+ self.cache_timestamp[cache_key] = time.time()
382
+
383
+ return candidate_data
384
+ else:
385
+ return None
386
+
387
+ except (asyncio.TimeoutError, Exception) as e:
388
+ if "rate limit" not in str(e).lower():
389
+ continue
390
+ return None
391
 
392
+ def _quick_validation(self, current_price, dollar_volume, high_24h, low_24h):
393
+ """تحقق سريع من المعايير الأساسية"""
394
+ return all([
395
+ dollar_volume >= 1000000, # حجم تداول لا يقل عن 1M دولار
396
+ current_price > 0.00000001, # أي سعر مقبول
397
+ current_price <= 100000, # حد أقصى معقول
398
+ high_24h > 0, # بيانات سعر صالحة
399
+ low_24h > 0,
400
+ high_24h != low_24h # تجنب الأسعار الثابتة
401
+ ])
 
 
 
402
 
403
+ def _calculate_fast_layer1_score(self, dollar_volume, price_change, current_price, high_24h, low_24h):
404
+ """حساب سريع لدرجة الطبقة 1"""
405
+ # حساب مبسط للحجم (بدون تعقيد)
406
+ volume_score = min(dollar_volume / 5000000, 1.0) # تطبيع إلى 5M
407
 
408
+ # حساب مبكر للزخم
409
+ if price_change >= 15:
410
+ momentum_score = 0.9
411
+ elif price_change >= 8:
412
+ momentum_score = 0.7
413
+ elif price_change >= 3:
414
+ momentum_score = 0.5
415
+ elif price_change <= -15:
416
+ momentum_score = 0.8 # فرصة شراء في الانخفاض
417
+ elif price_change <= -8:
418
+ momentum_score = 0.6
419
+ elif price_change <= -3:
420
+ momentum_score = 0.4
421
+ else:
422
+ momentum_score = 0.3
423
 
424
+ # حساب موقع السعر بشكل مبسط
425
+ if high_24h != low_24h:
426
+ position = (current_price - low_24h) / (high_24h - low_24h)
427
+ if position < 0.3:
428
+ position_score = 0.8
429
+ elif position > 0.7:
430
+ position_score = 0.6
431
+ else:
432
+ position_score = 0.5
433
  else:
434
+ position_score = 0.5
435
+
436
+ # الدرجة النهائية المبسطة
437
+ return (volume_score * 0.4 + momentum_score * 0.4 + position_score * 0.2)
438
 
439
+ def _generate_fast_reasons(self, dollar_volume, price_change):
440
+ """توليد أسباب سريعة"""
 
441
  reasons = []
442
 
443
+ if dollar_volume >= 5000000:
444
+ reasons.append('very_high_volume')
445
+ elif dollar_volume >= 2000000:
446
+ reasons.append('high_volume')
447
+ else:
448
  reasons.append('good_liquidity')
449
 
450
  if price_change >= 8:
 
456
  elif price_change <= -3:
457
  reasons.append('dip_opportunity')
458
 
 
 
 
 
 
459
  return reasons
460
 
461
+ def _validate_cached_data(self, cached_data):
462
+ """التحقق من صحة البيانات المخزنة"""
463
+ if not cached_data:
464
+ return False
465
+
466
+ # التحقق من عمر البيانات
467
+ cache_key = f"ticker_{cached_data['symbol']}"
468
+ cache_time = self.cache_timestamp.get(cache_key, 0)
469
+ if time.time() - cache_time > self.cache_duration:
470
+ return False
471
+
472
+ # التحقق من صحة البيانات
473
+ required_fields = ['symbol', 'current_price', 'dollar_volume', 'layer1_score']
474
+ return all(field in cached_data for field in required_fields)
475
+
476
  async def get_ohlcv_data_for_symbols(self, symbols: List[str]) -> List[Dict[str, Any]]:
477
  """
478
+ جلب بيانات OHLCV كاملة للرموز المحددة بشكل متواز
 
479
  """
480
  results = []
481
 
482
  print(f"📊 جلب بيانات OHLCV لـ {len(symbols)} عملة...")
483
 
484
+ # استخدام المهام المتوازية لجلب البيانات
485
+ tasks = []
486
  for symbol in symbols:
487
+ task = asyncio.create_task(self._fetch_ohlcv_for_symbol(symbol))
488
+ tasks.append(task)
489
+
490
+ # معالجة النتائج مع تحديد مهلة
491
+ for task in asyncio.as_completed(tasks, timeout=30):
492
  try:
493
+ result = await task
494
+ if result:
495
+ results.append(result)
496
+ except (asyncio.TimeoutError, Exception) as e:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  continue
498
 
499
  print(f"✅ تم تجميع بيانات OHLCV لـ {len(results)} عملة")
500
  return results
501
 
502
+ async def _fetch_ohlcv_for_symbol(self, symbol: str):
503
+ """جلب بيانات OHLCV لرمز واحد"""
504
+ try:
505
+ ohlcv_data = {}
506
+ timeframes = [
507
+ ('5m', 50), # تقليل عدد الشموع
508
+ ('15m', 50),
509
+ ('1h', 50),
510
+ ('4h', 50),
511
+ ('1d', 30), # تقليل للبيانات اليومية
512
+ ]
513
+
514
+ has_sufficient_data = True
515
+ for timeframe, limit in timeframes:
516
+ try:
517
+ ohlcv = await asyncio.wait_for(
518
+ self.exchange.fetch_ohlcv(symbol, timeframe, limit=limit),
519
+ timeout=5
520
+ )
521
+ if ohlcv and len(ohlcv) >= 15: # تقليل الحد الأدنى
522
+ ohlcv_data[timeframe] = ohlcv
523
+ else:
524
+ has_sufficient_data = False
525
+ break
526
+ except (asyncio.TimeoutError, Exception):
527
+ has_sufficient_data = False
528
+ break
529
+
530
+ if has_sufficient_data:
531
+ # جلب السعر الحالي بشكل منفصل
532
+ ticker = await asyncio.wait_for(
533
+ self.exchange.fetch_ticker(symbol),
534
+ timeout=3
535
+ )
536
+ current_price = ticker.get('last', 0) if ticker else 0
537
+
538
+ return {
539
+ 'symbol': symbol,
540
+ 'ohlcv': ohlcv_data,
541
+ 'current_price': current_price,
542
+ 'timestamp': datetime.now().isoformat()
543
+ }
544
+ else:
545
+ return None
546
+
547
+ except Exception as symbol_error:
548
+ return None
549
+
550
  async def get_latest_price_async(self, symbol):
551
  """جلب السعر الحالي لعملة محددة"""
552
  try:
553
  if not self.exchange:
554
  return None
555
 
556
+ # التحقق من التخزين المؤقت أولاً
557
+ cache_key = f"price_{symbol}"
558
+ if (cache_key in self.symbol_cache and
559
+ time.time() - self.cache_timestamp.get(cache_key, 0) < 10): # تخزين الأسعار لمدة 10 ثوان
560
+ return self.symbol_cache[cache_key].get('current_price')
561
+
562
+ ticker = await asyncio.wait_for(
563
+ self.exchange.fetch_ticker(symbol),
564
+ timeout=3
565
+ )
566
  current_price = ticker.get('last')
567
 
568
  if current_price:
569
+ price = float(current_price)
570
+ # تحديث التخزين المؤقت
571
+ self.symbol_cache[cache_key] = {'current_price': price}
572
+ self.cache_timestamp[cache_key] = time.time()
573
+ return price
574
  else:
575
  return None
576
 
577
  except Exception as e:
 
578
  return None
579
 
580
  async def get_available_symbols(self):
 
612
  print(f"❌ خطأ في التحقق من الرمز {symbol}: {e}")
613
  return False
614
 
615
+ def clear_cache(self):
616
+ """مسح الذاكرة المؤقتة"""
617
+ self.symbol_cache.clear()
618
+ self.cache_timestamp.clear()
619
+ print("✅ تم مسح الذاكرة المؤقتة")
620
+
621
+ print("✅ DataManager loaded - Optimized for high performance screening")