Riy777 commited on
Commit
ed04390
·
1 Parent(s): c6f72fe

Update data_manager.py

Browse files
Files changed (1) hide show
  1. data_manager.py +830 -291
data_manager.py CHANGED
@@ -1,14 +1,15 @@
1
- # data_manager.py
2
- import os, asyncio, httpx, json, traceback, backoff, re, time
3
  from datetime import datetime, timedelta
4
  from functools import wraps
 
5
  import ccxt.pro as ccxt
6
  from ccxt.base.errors import RateLimitExceeded, DDoSProtection, NetworkError
7
  import pandas as pd
8
  import numpy as np
9
  from state import MARKET_STATE_OK
10
 
11
- # --- 🐋 نظام تتبع الحيتان المحسن مع إصلاحات APIs ---
12
  class EnhancedWhaleMonitor:
13
  def __init__(self, contracts_db=None):
14
  self.http_client = httpx.AsyncClient(timeout=15.0, limits=httpx.Limits(max_connections=50, max_keepalive_connections=10))
@@ -21,7 +22,29 @@ class EnhancedWhaleMonitor:
21
  self.whale_threshold_usd = 100000
22
  self.contracts_db = contracts_db or {}
23
 
24
- # 🔄 إحصائيات استخدام APIs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  self.api_usage_stats = {
26
  'etherscan': {
27
  'requests_today': 0,
@@ -37,7 +60,7 @@ class EnhancedWhaleMonitor:
37
  }
38
  }
39
 
40
- # 🔄 مصادر RPC متعددة
41
  self.rpc_endpoints = {
42
  'ethereum': [
43
  'https://rpc.ankr.com/eth',
@@ -48,29 +71,14 @@ class EnhancedWhaleMonitor:
48
  'https://bsc-dataseed.binance.org/',
49
  'https://bsc-dataseed1.defibit.io/',
50
  'https://bsc-dataseed1.ninicoin.io/'
51
- ],
52
- 'polygon': [
53
- 'https://polygon-rpc.com/',
54
- 'https://rpc-mainnet.maticvigil.com',
55
- 'https://polygon-bor.publicnode.com'
56
- ],
57
- 'arbitrum': [
58
- 'https://arb1.arbitrum.io/rpc',
59
- 'https://endpoints.omniatech.io/v1/arbitrum/one/public'
60
- ],
61
- 'avalanche': [
62
- 'https://api.avax.network/ext/bc/C/rpc',
63
- 'https://avalanche.public-rpc.com'
64
  ]
65
  }
66
 
67
- # إضافة Infura إلى القائمة إذا كان المفتاح متوفرًا
68
  if self.infura_key:
69
  infura_endpoint = f"https://mainnet.infura.io/v3/{self.infura_key}"
70
  self.rpc_endpoints['ethereum'].insert(0, infura_endpoint)
71
  print(f"✅ تم تكوين Infura بنجاح - الشبكة: Ethereum")
72
- else:
73
- print("⚠️ مفتاح Infura غير متوفر في متغيرات البيئة - استخدام المصادر البديلة")
74
 
75
  self.current_rpc_index = {network: 0 for network in self.rpc_endpoints.keys()}
76
  self.rpc_failures = {network: 0 for network in self.rpc_endpoints.keys()}
@@ -78,103 +86,487 @@ class EnhancedWhaleMonitor:
78
  self.price_cache = {}
79
  self.last_scan_time = {}
80
 
81
- # 🔧 تعيين رموز KuCoin الصحيحة للعملات
82
  self.kucoin_symbols = {
83
  'ethereum': 'ETH',
84
- 'bsc': 'BNB',
85
- 'polygon': 'MATIC',
86
- 'arbitrum': 'ETH',
87
- 'avalanche': 'AVAX'
88
  }
89
-
90
- def _update_api_usage_stats(self, api_name):
91
- """تحديث إحصائيات استخدام API"""
92
- now = datetime.now()
93
- current_date = now.date()
94
-
95
- stats = self.api_usage_stats[api_name]
96
 
97
- # 🔄 إعادة تعيين العداد اليومي إذا تغير التاريخ
98
- if current_date != stats['last_reset']:
99
- stats['requests_today'] = 0
100
- stats['last_reset'] = current_date
101
- print(f"🔄 إعادة تعيين عداد طلبات {api_name} اليومي")
 
 
 
 
 
 
 
 
 
102
 
103
- # 📊 تحديث طلبات الثانية
104
- current_time = time.time()
105
- time_diff = current_time - stats['last_request_time']
 
 
 
 
106
 
107
- if time_diff < 1.0:
108
- stats['requests_per_second'] += 1
109
- else:
110
- stats['requests_per_second'] = 1
111
- stats['last_request_time'] = current_time
 
 
 
 
 
 
 
 
 
112
 
113
- stats['requests_today'] += 1
 
 
114
 
115
- # 🚨 التحقق من الحدود
116
- if api_name == 'etherscan':
117
- if stats['requests_today'] > 90000: # تحذير عند الاقتراب من 90,000
118
- print(f"🚨 تحذير: طلبات {api_name} اليومية تقترب من الحد ({stats['requests_today']}/100,000)")
119
- if stats['requests_per_second'] > 4: # تحذير عند الاقتراب من 4/ثانية
120
- print(f"🚨 تحذير: طلبات {api_name} في الثانية تقترب من الحد ({stats['requests_per_second']}/5)")
 
 
 
 
 
 
 
 
 
 
121
 
122
- elif api_name == 'infura':
123
- if stats['requests_today'] > 2500000:
124
- print(f"🚨 تحذير: طلبات {api_name} اليومية تقترب من الحد ({stats['requests_today']}/3,000,000)")
125
- if stats['requests_per_second'] > 450:
126
- print(f"🚨 تحذير: طلبات {api_name} في الثانية تقترب من الحد ({stats['requests_per_second']}/500)")
127
 
128
- async def _api_rate_limit_delay(self, api_name):
129
- """تأخير ذكي لتجنب تجاوز حدود API"""
130
- stats = self.api_usage_stats[api_name]
 
131
 
132
- if api_name == 'etherscan':
133
- # التحقق من طلبات الثانية
134
- if stats['requests_per_second'] > 4:
135
- delay = 0.2 * (stats['requests_per_second'] - 4)
136
- print(f"⏳ تأخير {delay:.2f} ثانية لـ {api_name} لتجنب rate limiting")
137
- await asyncio.sleep(delay)
138
-
139
- # التحقق من الطلبات اليومية
140
- if stats['requests_today'] > 95000:
141
- print(f"🚨 تجاوز الحد اليومي لطلبات {api_name}، استخدام المصادر البديلة")
142
- return True
143
 
144
- elif api_name == 'infura':
145
- if stats['requests_per_second'] > 400:
146
- delay = 0.1 * (stats['requests_per_second'] - 400)
147
- print(f"⏳ تأخير {delay:.2f} ثانية لـ {api_name} لتجنب rate limiting")
148
- await asyncio.sleep(delay)
149
-
150
- if stats['requests_today'] > 2800000:
151
- print(f"🚨 تجاوز الحد اليومي لطلبات {api_name}، استخدام المصادر البديلة")
152
- return True
153
 
154
  return False
155
 
156
- def _get_next_rpc_endpoint(self, network):
157
- """الحصول على عنوان RPC التالي"""
158
- if network not in self.rpc_endpoints:
159
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
- endpoints = self.rpc_endpoints[network]
162
- if not endpoints:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  return None
 
 
 
 
 
164
 
165
- # 🔄 التدوير العادي للمصادر
166
- index = self.current_rpc_index[network]
167
- endpoint = endpoints[index]
168
- self.current_rpc_index[network] = (index + 1) % len(endpoints)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
 
170
- return endpoint
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
  async def _get_native_coin_price(self, network):
173
- """جلب الأسعار من KuCoin (عام) مع إصلاح الرموز"""
174
  now = time.time()
175
  cache_key = f"{network}_price"
176
 
177
- # التحقق من التخزين المؤقت (5 دقائق)
178
  if cache_key in self.price_cache and (now - self.price_cache[cache_key]['timestamp']) < 300:
179
  return self.price_cache[cache_key]['price']
180
 
@@ -183,13 +575,11 @@ class EnhancedWhaleMonitor:
183
  return await self._get_price_from_coingecko_fallback(network)
184
 
185
  try:
186
- # 🔄 المحاولة الأولى: KuCoin (عام)
187
  price = await self._get_price_from_kucoin(symbol)
188
  if price and price > 0:
189
  self.price_cache[cache_key] = {'price': price, 'timestamp': now, 'source': 'kucoin'}
190
  return price
191
 
192
- # 🔄 المحاولة الثانية: CoinGecko
193
  price = await self._get_price_from_coingecko_fallback(network)
194
  if price and price > 0:
195
  self.price_cache[cache_key] = {'price': price, 'timestamp': now, 'source': 'coingecko'}
@@ -202,15 +592,13 @@ class EnhancedWhaleMonitor:
202
  return None
203
 
204
  async def _get_price_from_kucoin(self, symbol):
205
- """جلب السعر من KuCoin (عام بدون API key)"""
206
  try:
207
- # 🔧 استخدام KuCoin بدون API keys (عام)
208
  exchange = ccxt.kucoin({
209
  'sandbox': False,
210
  'enableRateLimit': True
211
  })
212
 
213
- # 🔧 المحاولة برمز USDT أولاً
214
  trading_symbol = f"{symbol}/USDT"
215
  try:
216
  ticker = await exchange.fetch_ticker(trading_symbol)
@@ -233,10 +621,7 @@ class EnhancedWhaleMonitor:
233
  """الاحتياطي: جلب السعر من CoinGecko"""
234
  coin_map = {
235
  'ethereum': 'ethereum',
236
- 'bsc': 'binancecoin',
237
- 'polygon': 'matic-network',
238
- 'arbitrum': 'ethereum',
239
- 'avalanche': 'avalanche-2'
240
  }
241
 
242
  coin_id = coin_map.get(network)
@@ -258,7 +643,7 @@ class EnhancedWhaleMonitor:
258
  return None
259
 
260
  async def _call_rpc_async(self, network, method, params=[]):
261
- """اتصال RPC غير متزامن مع إدارة ذكية للطلبات"""
262
  max_retries = 2
263
 
264
  for attempt in range(max_retries):
@@ -268,14 +653,11 @@ class EnhancedWhaleMonitor:
268
  return None
269
 
270
  try:
271
- # 🎯 إدارة طلبات Infura
272
  if 'infura' in endpoint and self.infura_key:
273
  self._update_api_usage_stats('infura')
274
 
275
  if await self._api_rate_limit_delay('infura'):
276
  continue
277
-
278
- print(f"🌐 استخدام Infura للشبكة {network}")
279
 
280
  payload = {"jsonrpc": "2.0", "method": method, "params": params, "id": 1}
281
 
@@ -285,11 +667,11 @@ class EnhancedWhaleMonitor:
285
  response = await client.post(endpoint, json=payload)
286
 
287
  if response.status_code == 401:
288
- print(f"🔐 خطأ مصادقة في {endpoint} - تخطي هذا المصدر")
289
  self.rpc_failures[network] += 1
290
  continue
291
  elif response.status_code == 429:
292
- print(f"⏳ Rate limit على {endpoint} - تأخير وإعادة المحاولة")
293
  await asyncio.sleep(2 * (attempt + 1))
294
  continue
295
 
@@ -301,20 +683,20 @@ class EnhancedWhaleMonitor:
301
 
302
  except httpx.HTTPStatusError as e:
303
  if e.response.status_code == 429:
304
- print(f"⚠️ Rate limit على {endpoint} لـ {network}. المحاولة {attempt + 1}/{max_retries}")
305
  self.rpc_failures[network] += 1
306
  await asyncio.sleep(3 * (attempt + 1))
307
  continue
308
  elif e.response.status_code == 401:
309
- print(f"🔐 خطأ مصادقة في {endpoint} - تخطي هذا المصدر")
310
  self.rpc_failures[network] += 1
311
  continue
312
  else:
313
- print(f"⚠️ خطأ HTTP {e.response.status_code} ف�� {endpoint} لـ {network}: {e}")
314
  self.rpc_failures[network] += 1
315
 
316
  except Exception as e:
317
- print(f"⚠️ فشل اتصال RPC لـ {network} على {endpoint}: {e}")
318
  self.rpc_failures[network] += 1
319
 
320
  if attempt < max_retries - 1:
@@ -323,145 +705,80 @@ class EnhancedWhaleMonitor:
323
  print(f"❌ فشل جميع محاولات RPC لـ {network}")
324
  return None
325
 
326
- async def _scan_single_evm_network(self, network):
327
- """مسح شبكة EVM واحدة مع تحسين كفاءة الطلبات"""
328
- whale_alerts = []
329
- try:
330
- price_usd = await self._get_native_coin_price(network)
331
- if price_usd is None:
332
- print(f"⚠️ سعر {network} غير متوفر، تخطي المسح")
333
- return []
334
-
335
- latest_block_hex = await self._call_rpc_async(network, 'eth_blockNumber')
336
- if not latest_block_hex:
337
- return []
338
-
339
- latest_block = int(latest_block_hex, 16)
340
-
341
- blocks_to_scan = 10
342
- scanned_blocks = 0
343
-
344
- for block_offset in range(blocks_to_scan):
345
- block_number = latest_block - block_offset
346
- if block_number < 0:
347
- break
348
-
349
- block_data = await self._call_rpc_async(network, 'eth_getBlockByNumber', [hex(block_number), True])
350
- if not block_data or 'transactions' not in block_data:
351
- continue
352
-
353
- scanned_blocks += 1
354
-
355
- block_timestamp_hex = block_data.get('timestamp', '0x0')
356
- block_timestamp = int(block_timestamp_hex, 16)
357
- block_time = datetime.fromtimestamp(block_timestamp)
358
- time_ago = datetime.now() - block_time
359
-
360
- for tx in block_data.get('transactions', []):
361
- value_wei = int(tx.get('value', '0x0'), 16)
362
- if value_wei > 0:
363
- value_native = value_wei / 1e18
364
- value_usd = value_native * price_usd
365
- if value_usd >= self.whale_threshold_usd:
366
- whale_alerts.append({
367
- 'network': network,
368
- 'value_usd': value_usd,
369
- 'from': tx.get('from'),
370
- 'to': tx.get('to'),
371
- 'hash': tx.get('hash'),
372
- 'block_number': block_number,
373
- 'timestamp': block_timestamp,
374
- 'human_time': block_time.isoformat(),
375
- 'minutes_ago': time_ago.total_seconds() / 60,
376
- 'transaction_type': 'native_transfer'
377
- })
378
-
379
- if block_offset % 3 == 0:
380
- await asyncio.sleep(0.2)
381
-
382
- print(f"✅ مسح {network}: {scanned_blocks} كتل، {len(whale_alerts)} تنبيهات")
383
-
384
- except Exception as e:
385
- print(f"⚠️ خطأ في مسح شبكة {network}: {e}")
386
 
387
- return whale_alerts
 
 
 
 
 
 
 
 
388
 
389
- async def get_general_whale_activity(self):
390
- """الوظيفة الرئيسية لمراقبة الحيتان"""
391
- print("🌊 بدء مراقبة الحيتان عبر الشبكات المتعددة...")
 
392
 
393
- try:
394
- tasks = []
395
- networks_to_scan = ['ethereum', 'bsc']
396
- for network in networks_to_scan:
397
- tasks.append(self._scan_single_evm_network(network))
398
-
399
- results = await asyncio.gather(*tasks, return_exceptions=True)
400
-
401
- all_alerts = []
402
- successful_networks = 0
403
-
404
- for res in results:
405
- if isinstance(res, list):
406
- all_alerts.extend(res)
407
- successful_networks += 1
 
 
 
 
 
 
 
 
 
 
 
 
 
408
 
409
- all_alerts.sort(key=lambda x: x['timestamp'], reverse=True)
410
-
411
- total_volume = sum(alert['value_usd'] for alert in all_alerts)
412
- alert_count = len(all_alerts)
413
-
414
- if not all_alerts:
415
- return {
416
- 'data_available': False,
417
- 'description': 'لم يتم اكتشاف نشاط حيتان كبير على الشبكات المراقبة',
418
- 'critical_alert': False,
419
- 'sentiment': 'UNKNOWN',
420
- 'total_volume_usd': 0,
421
- 'transaction_count': 0,
422
- 'data_quality': 'HIGH',
423
- 'networks_scanned': successful_networks
424
- }
425
 
426
- latest_alert = all_alerts[0] if all_alerts else None
427
- latest_time_info = f"آخر نشاط منذ {latest_alert['minutes_ago']:.1f} دقيقة" if latest_alert else ""
428
-
429
- sentiment = 'BULLISH' if total_volume > 5_000_000 else 'SLIGHTLY_BULLISH' if total_volume > 1_000_000 else 'NEUTRAL'
430
- critical = total_volume > 10_000_000 or any(tx['value_usd'] > 5_000_000 for tx in all_alerts)
431
-
432
- description = f"تم اكتشاف {alert_count} معاملة حوت بإجمالي ${total_volume:,.0f} عبر {successful_networks} شبكات. {latest_time_info}"
433
- print(f" {description}")
434
-
435
- return {
436
- 'data_available': True,
437
- 'description': description,
438
- 'critical_alert': critical,
439
- 'sentiment': sentiment,
440
- 'total_volume_usd': total_volume,
441
- 'transaction_count': alert_count,
442
- 'recent_alerts': all_alerts[:5],
443
- 'latest_activity': latest_alert['human_time'] if latest_alert else None,
444
- 'time_analysis': {
445
- 'oldest_minutes': max(alert['minutes_ago'] for alert in all_alerts) if all_alerts else 0,
446
- 'newest_minutes': min(alert['minutes_ago'] for alert in all_alerts) if all_alerts else 0,
447
- 'average_minutes': sum(alert['minutes_ago'] for alert in all_alerts) / len(all_alerts) if all_alerts else 0
448
- },
449
- 'data_quality': 'HIGH',
450
- 'networks_scanned': successful_networks
451
- }
452
 
453
- except Exception as e:
454
- print(f" فشل مراقبة الحيتان العامة: {e}")
455
- return {
456
- 'data_available': False,
457
- 'description': f'فشل في مراقبة الحيتان: {str(e)}',
458
- 'critical_alert': False,
459
- 'sentiment': 'UNKNOWN',
460
- 'total_volume_usd': 0,
461
- 'transaction_count': 0,
462
- 'data_quality': 'LOW',
463
- 'error': str(e)
464
- }
465
 
466
  async def get_symbol_specific_whale_data(self, symbol, contract_address=None):
467
  """جلب بيانات الحيتان الخاصة بعملة محددة"""
@@ -474,7 +791,7 @@ class EnhancedWhaleMonitor:
474
  if not contract_address:
475
  return await self._scan_networks_for_symbol(symbol, base_symbol)
476
 
477
- print(f"🔍 جلب بيانات الحيتان لـ {symbol} مع العقد: {contract_address[:10]}...")
478
 
479
  api_data = await self._get_combined_api_data(contract_address)
480
 
@@ -488,7 +805,7 @@ class EnhancedWhaleMonitor:
488
  print(f"❌ فشل جلب بيانات الحيتان لـ {symbol}: {e}")
489
  return {
490
  'data_available': False,
491
- 'description': f'خطأ في جلب بيانات الحيتان: {str(e)}',
492
  'total_volume': 0,
493
  'transfer_count': 0,
494
  'source': 'error'
@@ -501,7 +818,7 @@ class EnhancedWhaleMonitor:
501
  if self.moralis_key:
502
  tasks.append(self._get_moralis_token_data(contract_address))
503
  if self.etherscan_key:
504
- tasks.append(self._get_etherscan_token_data_v2(contract_address)) # 🔄 استخدام الإصدار V2
505
 
506
  if not tasks:
507
  return []
@@ -516,47 +833,42 @@ class EnhancedWhaleMonitor:
516
  return all_transfers
517
 
518
  async def _get_etherscan_token_data_v2(self, contract_address):
519
- """🔧 إصلاح: جلب بيانات Etherscan باستخدام الإصدار V2 من API"""
520
  if not self.etherscan_key:
521
  return []
522
 
523
  try:
524
- # 🔄 تحديث إحصائيات الاستخدام
525
  self._update_api_usage_stats('etherscan')
526
 
527
- # ⏳ التحقق من حدود الاستخدام
528
  if await self._api_rate_limit_delay('etherscan'):
529
  print("⚠️ تجاوز حدود Etherscan، تخطي الطلب")
530
  return []
531
 
532
- # 🔧 استخدام معاملات الإصدار V2 حسب الوثائق
533
  params = {
534
  "module": "account",
535
  "action": "tokentx",
536
  "contractaddress": contract_address,
537
  "page": 1,
538
- "offset": 10, # تقليل العدد لتوفير الطلبات
539
  "sort": "desc",
540
  "apikey": self.etherscan_key
541
  }
542
 
543
- # 🔧 استخدام base URL الصحيح
544
  base_url = "https://api.etherscan.io/api"
545
 
546
- print(f"🔍 جلب بيانات Etherscan V2 للعقد: {contract_address[:10]}...")
547
 
548
  async with httpx.AsyncClient(timeout=10.0) as client:
549
  response = await client.get(base_url, params=params)
550
 
551
  if response.status_code == 429:
552
- print("⏳ تجاوز حد معدل Etherscan، تأخير الطلبات المستقبلية")
553
  await asyncio.sleep(2)
554
  return []
555
 
556
  response.raise_for_status()
557
  data = response.json()
558
 
559
- # 🔧 التحقق من استجابة API
560
  if data.get('status') == '1' and data.get('message') == 'OK':
561
  result = data.get('result', [])
562
  print(f"✅ بيانات Etherscan: {len(result)} تحويل")
@@ -582,7 +894,7 @@ class EnhancedWhaleMonitor:
582
  response = await self.http_client.get(
583
  f"https://deep-index.moralis.io/api/v2/erc20/{contract_address}/transfers",
584
  headers={"X-API-Key": self.moralis_key},
585
- params={"chain": "eth", "limit": 10} # تقليل العدد
586
  )
587
 
588
  if response.status_code == 200:
@@ -597,7 +909,136 @@ class EnhancedWhaleMonitor:
597
  print(f"⚠️ Moralis API error: {e}")
598
  return []
599
 
600
- # ... باقي الدوال تبقى كما كانت ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
 
602
  def get_api_usage_stats(self):
603
  """الحصول على إحصائيات استخدام APIs"""
@@ -632,12 +1073,10 @@ class DataManager:
632
  def __init__(self, contracts_db):
633
  self.contracts_db = contracts_db or {}
634
 
635
- # 🔧 استخدام KuCoin بدون API keys (عام)
636
  try:
637
  self.exchange = ccxt.kucoin({
638
  'sandbox': False,
639
  'enableRateLimit': True
640
- # لا حاجة لـ API keys للاستخدام العام
641
  })
642
  self.exchange.rateLimit = 800
643
  print("✅ تم تهيئة KuCoin (الوضع العام)")
@@ -654,29 +1093,16 @@ class DataManager:
654
  async def initialize(self):
655
  self.http_client = httpx.AsyncClient(timeout=20.0)
656
 
657
- # التحقق من توفر مفاتيح API
658
  api_status = {
659
  'KUCOIN': '🟢 عام (بدون مفتاح)',
660
  'MORALIS_KEY': "🟢 متوفر" if os.getenv('MORALIS_KEY') else "🔴 غير متوفر",
661
- 'ETHERSCAN_KEY': "🟢 متوفر" if os.getenv('ETHERSCAN_KEY') else "🔴 غير متوفر",
662
  'INFURA_KEY': "🟢 متوفر" if os.getenv('INFURA_KEY') else "🔴 غير متوفر"
663
  }
664
 
665
- print("✅ DataManager initialized - API Status:")
666
  for key, status in api_status.items():
667
  print(f" {key}: {status}")
668
-
669
- # عرض حدود الاستخدام
670
- api_limits = {
671
- 'Etherscan': '5/ثانية, 100,000/يوم',
672
- 'Infura': '500/ثانية, 3,000,000/يوم',
673
- 'KuCoin': 'عام (بدون حدود)',
674
- 'CoinGecko': '50/دقيقة'
675
- }
676
-
677
- print("📊 حدود استخدام APIs:")
678
- for api, limit in api_limits.items():
679
- print(f" {api}: {limit}")
680
 
681
  async def close(self):
682
  if self.http_client:
@@ -685,7 +1111,7 @@ class DataManager:
685
  await self.exchange.close()
686
 
687
  async def get_sentiment_safe_async(self):
688
- """جلب بيانات المشاعر مع مصادر احتياطية"""
689
  max_retries = 2
690
  for attempt in range(max_retries):
691
  try:
@@ -713,7 +1139,7 @@ class DataManager:
713
  return None
714
 
715
  async def get_market_context_async(self):
716
- """جلب سياق السوق مع تتبع استخدام APIs"""
717
  max_retries = 2
718
  for attempt in range(max_retries):
719
  try:
@@ -742,6 +1168,8 @@ class DataManager:
742
 
743
  market_trend = self._determine_market_trend(bitcoin_price, sentiment_data, general_whale_activity)
744
 
 
 
745
  market_context = {
746
  'timestamp': datetime.now().isoformat(),
747
  'bitcoin_price_usd': bitcoin_price,
@@ -750,23 +1178,34 @@ class DataManager:
750
  'sentiment_class': sentiment_data.get('feargreed_class') if sentiment_data else 'UNKNOWN',
751
  'general_whale_activity': general_whale_activity or {
752
  'data_available': False,
753
- 'description': 'فشل في مراقبة الحيتان',
754
  'critical_alert': False,
755
- 'sentiment': 'UNKNOWN'
 
756
  },
757
  'market_trend': market_trend,
 
758
  'btc_sentiment': self._get_btc_sentiment(bitcoin_price),
759
  'data_sources': {
760
  'prices': bitcoin_price is not None and ethereum_price is not None,
761
  'sentiment': sentiment_data is not None,
762
- 'general_whale_data': general_whale_activity.get('data_available', False) if general_whale_activity else False
 
763
  },
764
  'data_quality': 'HIGH',
765
- 'api_usage': self.whale_monitor.get_api_usage_stats()
766
  }
767
 
768
  print(f"📊 سياق السوق: BTC=${bitcoin_price:,.0f}, ETH=${ethereum_price:,.0f}")
769
 
 
 
 
 
 
 
 
 
770
  return market_context
771
 
772
  except Exception as e:
@@ -776,6 +1215,104 @@ class DataManager:
776
 
777
  return self._get_minimal_market_context()
778
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
779
  def _get_btc_sentiment(self, bitcoin_price):
780
  """تحديد اتجاه البيتكوين"""
781
  if bitcoin_price is None:
@@ -788,7 +1325,7 @@ class DataManager:
788
  return 'NEUTRAL'
789
 
790
  async def _get_prices_with_fallback(self):
791
- """جلب الأسعار مع KuCoin العام"""
792
  try:
793
  prices = await self._get_prices_from_kucoin_safe()
794
  if prices.get('bitcoin') and prices.get('ethereum'):
@@ -805,7 +1342,7 @@ class DataManager:
805
  return {'bitcoin': None, 'ethereum': None}
806
 
807
  async def _get_prices_from_kucoin_safe(self):
808
- """جلب الأسعار من KuCoin (عام)"""
809
  if not self.exchange:
810
  return {'bitcoin': None, 'ethereum': None}
811
 
@@ -869,13 +1406,13 @@ class DataManager:
869
  'timestamp': datetime.now().isoformat(),
870
  'data_available': False,
871
  'data_sources': {'prices': False, 'sentiment': False, 'general_whale_data': False},
872
- 'error': 'فشل في جلب بيانات السوق من المصادر الخارجية',
873
  'market_trend': 'UNKNOWN',
874
  'btc_sentiment': 'UNKNOWN',
875
  'data_quality': 'LOW',
876
  'general_whale_activity': {
877
  'data_available': False,
878
- 'description': 'فشل في مراقبة الحيتان',
879
  'critical_alert': False,
880
  'sentiment': 'UNKNOWN'
881
  },
@@ -883,7 +1420,7 @@ class DataManager:
883
  'ethereum_price_usd': None,
884
  'fear_and_greed_index': None,
885
  'sentiment_class': 'UNKNOWN',
886
- 'missing_data': ['أسعار البيتكوين', 'أسعار الإيثيريوم', 'بيانات المشاعر', 'بيانات الحيتان']
887
  }
888
 
889
  def _determine_market_trend(self, bitcoin_price, sentiment_data, whale_activity):
@@ -936,8 +1473,6 @@ class DataManager:
936
  print(f"⚠️ خطأ في تحديد اتجاه السوق: {e}")
937
  return "UNKNOWN"
938
 
939
- # ... باقي دوال DataManager تبقى كما هي ...
940
-
941
  def get_performance_stats(self):
942
  """الحصول على إحصائيات الأداء"""
943
  total_attempts = self.fetch_stats['successful_fetches'] + self.fetch_stats['failed_fetches']
@@ -958,4 +1493,8 @@ class DataManager:
958
 
959
  return stats
960
 
961
- print("✅ Enhanced Data Manager Loaded - KUCOIN PUBLIC + ETHERSCAN V2 FIX + API RATE LIMITING")
 
 
 
 
 
1
+ # data_manager.py - الإصدار المحسن مع تحليل صافي التدفق الذكي
2
+ import os, asyncio, httpx, json, traceback, backoff, re, time, math
3
  from datetime import datetime, timedelta
4
  from functools import wraps
5
+ from collections import defaultdict, deque
6
  import ccxt.pro as ccxt
7
  from ccxt.base.errors import RateLimitExceeded, DDoSProtection, NetworkError
8
  import pandas as pd
9
  import numpy as np
10
  from state import MARKET_STATE_OK
11
 
12
+ # --- 🐋 نظام تتبع الحيتان المحسن مع تحليل صافي التدفق ---
13
  class EnhancedWhaleMonitor:
14
  def __init__(self, contracts_db=None):
15
  self.http_client = httpx.AsyncClient(timeout=15.0, limits=httpx.Limits(max_connections=50, max_keepalive_connections=10))
 
22
  self.whale_threshold_usd = 100000
23
  self.contracts_db = contracts_db or {}
24
 
25
+ # قاعدة بيانات العناوين المصنفة ديناميكياً
26
+ self.address_labels = {}
27
+ self._initialize_dynamic_labels()
28
+
29
+ # تخزين بيانات صافي التدفق
30
+ self.netflow_data = {
31
+ 'ethereum': defaultdict(lambda: {
32
+ 'inflow': deque(maxlen=288),
33
+ 'outflow': deque(maxlen=288),
34
+ 'netflow': deque(maxlen=288),
35
+ 'timestamps': deque(maxlen=288),
36
+ 'volume_24h': 0
37
+ }),
38
+ 'bsc': defaultdict(lambda: {
39
+ 'inflow': deque(maxlen=288),
40
+ 'outflow': deque(maxlen=288),
41
+ 'netflow': deque(maxlen=288),
42
+ 'timestamps': deque(maxlen=288),
43
+ 'volume_24h': 0
44
+ })
45
+ }
46
+
47
+ # إحصائيات استخدام APIs
48
  self.api_usage_stats = {
49
  'etherscan': {
50
  'requests_today': 0,
 
60
  }
61
  }
62
 
63
+ # مصادر RPC متعددة
64
  self.rpc_endpoints = {
65
  'ethereum': [
66
  'https://rpc.ankr.com/eth',
 
71
  'https://bsc-dataseed.binance.org/',
72
  'https://bsc-dataseed1.defibit.io/',
73
  'https://bsc-dataseed1.ninicoin.io/'
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  ]
75
  }
76
 
77
+ # إضافة Infura إذا كان المفتاح متوفرًا
78
  if self.infura_key:
79
  infura_endpoint = f"https://mainnet.infura.io/v3/{self.infura_key}"
80
  self.rpc_endpoints['ethereum'].insert(0, infura_endpoint)
81
  print(f"✅ تم تكوين Infura بنجاح - الشبكة: Ethereum")
 
 
82
 
83
  self.current_rpc_index = {network: 0 for network in self.rpc_endpoints.keys()}
84
  self.rpc_failures = {network: 0 for network in self.rpc_endpoints.keys()}
 
86
  self.price_cache = {}
87
  self.last_scan_time = {}
88
 
89
+ # رموز KuCoin للعملات
90
  self.kucoin_symbols = {
91
  'ethereum': 'ETH',
92
+ 'bsc': 'BNB'
 
 
 
93
  }
 
 
 
 
 
 
 
94
 
95
+ print("🎯 نظام تتبع الحيتان المحسن - تحليل صافي التدفق الذكي مفعل")
96
+
97
+ def _initialize_dynamic_labels(self):
98
+ """تهيئة التصنيفات الديناميكية للعناوين"""
99
+ # فئات التصنيف
100
+ self.address_categories = {
101
+ 'exchange': set(),
102
+ 'cex': set(),
103
+ 'dex': set(),
104
+ 'institution': set(),
105
+ 'whale': set(),
106
+ 'contract': set(),
107
+ 'unknown': set()
108
+ }
109
 
110
+ # أنماط العناوين للمنصات المركزية
111
+ self.exchange_patterns = {
112
+ 'binance': ['0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be', '0xd551234ae421e3bcba99a0da6d736074f22192ff'],
113
+ 'coinbase': ['0x71660c4005ba85c37ccec55d0c4493e66fe775d3', '0x503828976d22510aad0201ac7ec88293211d23da'],
114
+ 'kraken': ['0x2910543af39aba0cd09dbb2d50200b3e800a63d2', '0xa160cdab225685da1d56aa342ad8841c3b53f291'],
115
+ 'kucoin': ['0x2b5634c42055806a59e9107ed44d43c426e58258', '0x689c56aef474df92d44a1b70850f808488f9769c']
116
+ }
117
 
118
+ # تحميل العناوين الأولية
119
+ self._load_initial_exchange_addresses()
120
+
121
+ def _load_initial_exchange_addresses(self):
122
+ """تحميل عناوين المنصات المعروفة مبدئياً"""
123
+ for exchange, addresses in self.exchange_patterns.items():
124
+ for address in addresses:
125
+ self.address_labels[address.lower()] = 'cex'
126
+ self.address_categories['cex'].add(address.lower())
127
+ self.address_categories['exchange'].add(address.lower())
128
+
129
+ def _classify_address_dynamic(self, address, transaction_history=None):
130
+ """تصنيف ديناميكي للعناوين بناءً على الأنماط السلوكية"""
131
+ address_lower = address.lower()
132
 
133
+ # التحقق من العناوين المصنفة مسبقاً
134
+ if address_lower in self.address_labels:
135
+ return self.address_labels[address_lower]
136
 
137
+ # تصنيف بناءً على أنماط المعاملات
138
+ if transaction_history:
139
+ if self._detect_exchange_pattern(transaction_history):
140
+ self.address_labels[address_lower] = 'suspected_cex'
141
+ self.address_categories['cex'].add(address_lower)
142
+ return 'suspected_cex'
143
+
144
+ if self._detect_whale_pattern(transaction_history):
145
+ self.address_labels[address_lower] = 'suspected_whale'
146
+ self.address_categories['whale'].add(address_lower)
147
+ return 'suspected_whale'
148
+
149
+ if self._detect_contract_pattern(transaction_history):
150
+ self.address_labels[address_lower] = 'contract_user'
151
+ self.address_categories['contract'].add(address_lower)
152
+ return 'contract_user'
153
 
154
+ # إذا لم يتم التصنيف، نضيفه للمجهول
155
+ self.address_labels[address_lower] = 'unknown'
156
+ self.address_categories['unknown'].add(address_lower)
157
+ return 'unknown'
 
158
 
159
+ def _detect_exchange_pattern(self, transactions):
160
+ """اكتشاف نمط المنصات"""
161
+ if len(transactions) < 10:
162
+ return False
163
 
164
+ unique_senders = set()
165
+ unique_receivers = set()
 
 
 
 
 
 
 
 
 
166
 
167
+ for tx in transactions[-20:]:
168
+ if 'from' in tx:
169
+ unique_senders.add(tx['from'])
170
+ if 'to' in tx:
171
+ unique_receivers.add(tx['to'])
172
+
173
+ if len(unique_senders) > 15 and len(unique_receivers) < 5:
174
+ return True
 
175
 
176
  return False
177
 
178
+ def _detect_whale_pattern(self, transactions):
179
+ """اكتشاف نمط الحيتان"""
180
+ large_txs = [tx for tx in transactions if tx.get('value_usd', 0) > 100000]
181
+ return len(large_txs) >= 3
182
+
183
+ def _detect_contract_pattern(self, transactions):
184
+ """اكتشاف نمط العقود"""
185
+ contract_interactions = [tx for tx in transactions if tx.get('to', '') and len(tx.get('to', '')) == 42 and tx.get('input', '0x') != '0x']
186
+ return len(contract_interactions) > len(transactions) * 0.7
187
+
188
+ def _is_exchange_address(self, address):
189
+ """التحقق إذا كان العنوان ينتمي لمنصة"""
190
+ address_lower = address.lower()
191
+ return (address_lower in self.address_categories['cex'] or
192
+ address_lower in self.address_categories['exchange'] or
193
+ self.address_labels.get(address_lower) in ['cex', 'suspected_cex'])
194
+
195
+ async def _update_netflow_metrics(self, network, token_symbol, from_address, to_address, value_usd, transaction_hash):
196
+ """تحديث مقاييس صافي التدفق مع التصنيف الذكي"""
197
+ try:
198
+ # تصنيف العناوين
199
+ from_label = self._classify_address_dynamic(from_address)
200
+ to_label = self._classify_address_dynamic(to_address)
201
 
202
+ # تحديث التدفق الداخل إلى المنصات
203
+ if self._is_exchange_address(to_address):
204
+ if token_symbol not in self.netflow_data[network]:
205
+ self._initialize_token_metrics(network, token_symbol)
206
+
207
+ self.netflow_data[network][token_symbol]['inflow'].append(value_usd)
208
+ self.netflow_data[network][token_symbol]['timestamps'].append(datetime.now())
209
+
210
+ print(f"📥 تدفق إلى منصة: {value_usd:,.0f} USD ({token_symbol})")
211
+
212
+ # تحديث التدفق الخارج من المنصات
213
+ if self._is_exchange_address(from_address):
214
+ if token_symbol not in self.netflow_data[network]:
215
+ self._initialize_token_metrics(network, token_symbol)
216
+
217
+ self.netflow_data[network][token_symbol]['outflow'].append(value_usd)
218
+ self.netflow_data[network][token_symbol]['timestamps'].append(datetime.now())
219
+
220
+ print(f"📤 تدفق من منصة: {value_usd:,.0f} USD ({token_symbol})")
221
+
222
+ # حساب صافي التدفق الحالي
223
+ if token_symbol in self.netflow_data[network]:
224
+ current_inflow = sum(list(self.netflow_data[network][token_symbol]['inflow'])[-12:])
225
+ current_outflow = sum(list(self.netflow_data[network][token_symbol]['outflow'])[-12:])
226
+ current_netflow = current_inflow - current_outflow
227
+
228
+ self.netflow_data[network][token_symbol]['netflow'].append(current_netflow)
229
+
230
+ except Exception as e:
231
+ print(f"⚠️ خطأ في تحديث مقاييس صافي التدفق: {e}")
232
+
233
+ def _initialize_token_metrics(self, network, token_symbol):
234
+ """تهيئة مقاييس الرمز المميز"""
235
+ self.netflow_data[network][token_symbol] = {
236
+ 'inflow': deque(maxlen=288),
237
+ 'outflow': deque(maxlen=288),
238
+ 'netflow': deque(maxlen=288),
239
+ 'timestamps': deque(maxlen=288),
240
+ 'volume_24h': 0
241
+ }
242
+
243
+ def _calculate_netflow_zscore(self, network, token_symbol, window_hours=24):
244
+ """حساب Z-score لصافي التدفق"""
245
+ try:
246
+ if token_symbol not in self.netflow_data[network]:
247
+ return 0
248
+
249
+ data = self.netflow_data[network][token_symbol]
250
+ netflow_values = list(data['netflow'])
251
+
252
+ if len(netflow_values) < 10:
253
+ return 0
254
+
255
+ window_size = min(len(netflow_values), window_hours * 12)
256
+ recent_values = netflow_values[-window_size:]
257
+
258
+ if len(recent_values) < 5:
259
+ return 0
260
+
261
+ mean_val = np.mean(recent_values)
262
+ std_val = np.std(recent_values)
263
+
264
+ if std_val == 0:
265
+ return 0
266
+
267
+ current_netflow = recent_values[-1] if recent_values else 0
268
+ zscore = (current_netflow - mean_val) / std_val
269
+
270
+ return zscore
271
+
272
+ except Exception as e:
273
+ print(f"⚠️ خطأ في حساب Z-score: {e}")
274
+ return 0
275
+
276
+ def _generate_netflow_signal(self, network, token_symbol):
277
+ """توليد إشارات تداول بناءً على صافي التدفق"""
278
+ try:
279
+ if token_symbol not in self.netflow_data[network]:
280
+ return None
281
+
282
+ data = self.netflow_data[network][token_symbol]
283
+ netflow_values = list(data['netflow'])
284
+
285
+ if len(netflow_values) < 12:
286
+ return None
287
+
288
+ recent_inflow = sum(list(data['inflow'])[-12:])
289
+ recent_outflow = sum(list(data['outflow'])[-12:])
290
+ recent_netflow = recent_inflow - recent_outflow
291
+
292
+ zscore = self._calculate_netflow_zscore(network, token_symbol)
293
+
294
+ signal = {
295
+ 'symbol': token_symbol,
296
+ 'network': network,
297
+ 'netflow_1h': recent_netflow,
298
+ 'inflow_1h': recent_inflow,
299
+ 'outflow_1h': recent_outflow,
300
+ 'z_score': zscore,
301
+ 'timestamp': datetime.now().isoformat()
302
+ }
303
+
304
+ if recent_netflow < -500000 and zscore < -2.5:
305
+ signal.update({
306
+ 'action': 'STRONG_SELL',
307
+ 'confidence': min(0.95, abs(zscore) / 3),
308
+ 'reason': f'تدفق بيعي قوي: ${abs(recent_netflow):,.0f} إلى المنصات',
309
+ 'critical_alert': abs(recent_netflow) > 1000000
310
+ })
311
+ return signal
312
+
313
+ elif recent_netflow < -100000 and zscore < -1.5:
314
+ signal.update({
315
+ 'action': 'SELL',
316
+ 'confidence': min(0.8, abs(zscore) / 2),
317
+ 'reason': f'تدفق بيعي: ${abs(recent_netflow):,.0f} إلى المنصات',
318
+ 'critical_alert': False
319
+ })
320
+ return signal
321
+
322
+ elif recent_netflow > 500000 and zscore > 2.5:
323
+ signal.update({
324
+ 'action': 'STRONG_BUY',
325
+ 'confidence': min(0.95, zscore / 3),
326
+ 'reason': f'تدفق شرائي قوي: ${recent_netflow:,.0f} من المنصات',
327
+ 'critical_alert': recent_netflow > 1000000
328
+ })
329
+ return signal
330
+
331
+ elif recent_netflow > 100000 and zscore > 1.5:
332
+ signal.update({
333
+ 'action': 'BUY',
334
+ 'confidence': min(0.8, zscore / 2),
335
+ 'reason': f'تدفق شرائي: ${recent_netflow:,.0f} من المنصات',
336
+ 'critical_alert': False
337
+ })
338
+ return signal
339
+
340
+ signal.update({
341
+ 'action': 'HOLD',
342
+ 'confidence': 0.5,
343
+ 'reason': f'تدفق متوازن: ${recent_netflow:,.0f}',
344
+ 'critical_alert': False
345
+ })
346
+ return signal
347
+
348
+ except Exception as e:
349
+ print(f"⚠️ خطأ في توليد إشارة التداول: {e}")
350
  return None
351
+
352
+ async def _scan_single_evm_network(self, network):
353
+ """مسح شبكة EVM واحدة مع تحليل صافي التدفق"""
354
+ whale_alerts = []
355
+ trading_signals = []
356
 
357
+ try:
358
+ price_usd = await self._get_native_coin_price(network)
359
+ if price_usd is None:
360
+ print(f"⚠️ سعر {network} غير متوفر، تخطي المسح")
361
+ return [], []
362
+
363
+ latest_block_hex = await self._call_rpc_async(network, 'eth_blockNumber')
364
+ if not latest_block_hex:
365
+ return [], []
366
+
367
+ latest_block = int(latest_block_hex, 16)
368
+ blocks_to_scan = 15
369
+ scanned_blocks = 0
370
+
371
+ for block_offset in range(blocks_to_scan):
372
+ block_number = latest_block - block_offset
373
+ if block_number < 0:
374
+ break
375
+
376
+ block_data = await self._call_rpc_async(network, 'eth_getBlockByNumber', [hex(block_number), True])
377
+ if not block_data or 'transactions' not in block_data:
378
+ continue
379
+
380
+ scanned_blocks += 1
381
+
382
+ block_timestamp_hex = block_data.get('timestamp', '0x0')
383
+ block_timestamp = int(block_timestamp_hex, 16)
384
+ block_time = datetime.fromtimestamp(block_timestamp)
385
+ time_ago = datetime.now() - block_time
386
+
387
+ for tx in block_data.get('transactions', []):
388
+ value_wei = int(tx.get('value', '0x0'), 16)
389
+ if value_wei > 0:
390
+ value_native = value_wei / 1e18
391
+ value_usd = value_native * price_usd
392
+
393
+ if value_usd >= self.whale_threshold_usd:
394
+ from_address = tx.get('from', '')
395
+ to_address = tx.get('to', '')
396
+ tx_hash = tx.get('hash', '')
397
+
398
+ await self._update_netflow_metrics(network, 'NATIVE', from_address, to_address, value_usd, tx_hash)
399
+
400
+ from_label = self._classify_address_dynamic(from_address)
401
+ to_label = self._classify_address_dynamic(to_address)
402
+
403
+ whale_alerts.append({
404
+ 'network': network,
405
+ 'value_usd': value_usd,
406
+ 'from': from_address,
407
+ 'to': to_address,
408
+ 'from_label': from_label,
409
+ 'to_label': to_label,
410
+ 'hash': tx_hash,
411
+ 'block_number': block_number,
412
+ 'timestamp': block_timestamp,
413
+ 'human_time': block_time.isoformat(),
414
+ 'minutes_ago': time_ago.total_seconds() / 60,
415
+ 'transaction_type': 'native_transfer',
416
+ 'flow_direction': 'TO_EXCHANGE' if self._is_exchange_address(to_address) else
417
+ 'FROM_EXCHANGE' if self._is_exchange_address(from_address) else 'UNKNOWN'
418
+ })
419
+
420
+ if block_offset % 3 == 0:
421
+ await asyncio.sleep(0.1)
422
+
423
+ signal = self._generate_netflow_signal(network, 'NATIVE')
424
+ if signal:
425
+ trading_signals.append(signal)
426
+
427
+ print(f"✅ مسح {network}: {scanned_blocks} كتل، {len(whale_alerts)} تنبيهات، {len(trading_signals)} إشارات")
428
+
429
+ except Exception as e:
430
+ print(f"⚠️ خطأ في مسح شبكة {network}: {e}")
431
+
432
+ return whale_alerts, trading_signals
433
+
434
+ async def get_general_whale_activity(self):
435
+ """الوظيفة الرئيسية لمراقبة الحيتان"""
436
+ print("🌊 بدء مراقبة الحيتان وتحليل صافي التدفق...")
437
 
438
+ try:
439
+ tasks = []
440
+ networks_to_scan = ['ethereum', 'bsc']
441
+ for network in networks_to_scan:
442
+ tasks.append(self._scan_single_evm_network(network))
443
+
444
+ results = await asyncio.gather(*tasks, return_exceptions=True)
445
+
446
+ all_alerts = []
447
+ all_signals = []
448
+ successful_networks = 0
449
+
450
+ for res in results:
451
+ if isinstance(res, tuple) and len(res) == 2:
452
+ alerts, signals = res
453
+ all_alerts.extend(alerts)
454
+ all_signals.extend(signals)
455
+ successful_networks += 1
456
+
457
+ all_alerts.sort(key=lambda x: x['timestamp'], reverse=True)
458
+
459
+ total_volume = sum(alert['value_usd'] for alert in all_alerts)
460
+ alert_count = len(all_alerts)
461
+
462
+ exchange_inflow = sum(alert['value_usd'] for alert in all_alerts
463
+ if alert['flow_direction'] == 'TO_EXCHANGE')
464
+ exchange_outflow = sum(alert['value_usd'] for alert in all_alerts
465
+ if alert['flow_direction'] == 'FROM_EXCHANGE')
466
+ net_exchange_flow = exchange_inflow - exchange_outflow
467
+
468
+ critical_signals = [s for s in all_signals if s.get('critical_alert', False)]
469
+
470
+ if not all_alerts:
471
+ return {
472
+ 'data_available': False,
473
+ 'description': 'غير متوفر - لم يتم اكتشاف نشاط حيتان كبير',
474
+ 'critical_alert': False,
475
+ 'sentiment': 'UNKNOWN',
476
+ 'total_volume_usd': 0,
477
+ 'transaction_count': 0,
478
+ 'data_quality': 'HIGH',
479
+ 'networks_scanned': successful_networks,
480
+ 'trading_signals': all_signals,
481
+ 'netflow_analysis': {
482
+ 'inflow_to_exchanges': 0,
483
+ 'outflow_from_exchanges': 0,
484
+ 'net_flow': 0,
485
+ 'flow_direction': 'BALANCED'
486
+ }
487
+ }
488
+
489
+ latest_alert = all_alerts[0] if all_alerts else None
490
+ latest_time_info = f"آخر نشاط منذ {latest_alert['minutes_ago']:.1f} دقيقة" if latest_alert else ""
491
+
492
+ if net_exchange_flow < -1000000:
493
+ sentiment = 'BEARISH'
494
+ flow_description = f"ضغط بيعي قوي: ${abs(net_exchange_flow):,.0f} إلى المنصات"
495
+ market_impact = "HIGH"
496
+ elif net_exchange_flow < -500000:
497
+ sentiment = 'SLIGHTLY_BEARISH'
498
+ flow_description = f"ضغط بيعي: ${abs(net_exchange_flow):,.0f} إلى المنصات"
499
+ market_impact = "MEDIUM"
500
+ elif net_exchange_flow > 1000000:
501
+ sentiment = 'BULLISH'
502
+ flow_description = f"تراكم شرائي قوي: ${net_exchange_flow:,.0f} من المنصات"
503
+ market_impact = "HIGH"
504
+ elif net_exchange_flow > 500000:
505
+ sentiment = 'SLIGHTLY_BULLISH'
506
+ flow_description = f"تراكم شرائي: ${net_exchange_flow:,.0f} من المنصات"
507
+ market_impact = "MEDIUM"
508
+ else:
509
+ sentiment = 'NEUTRAL'
510
+ flow_description = f"تدفق متوازن: ${net_exchange_flow:,.0f} صافي"
511
+ market_impact = "LOW"
512
+
513
+ critical_alert = (
514
+ total_volume > 10_000_000 or
515
+ any(tx['value_usd'] > 5_000_000 for tx in all_alerts) or
516
+ abs(net_exchange_flow) > 5_000_000 or
517
+ len(critical_signals) > 0
518
+ )
519
+
520
+ description = f"تم اكتشاف {alert_count} معاملة حوت بإجمالي ${total_volume:,.0f} عبر {successful_networks} شبكات. {flow_description}. {latest_time_info}"
521
+
522
+ return {
523
+ 'data_available': True,
524
+ 'description': description,
525
+ 'critical_alert': critical_alert,
526
+ 'sentiment': sentiment,
527
+ 'market_impact': market_impact,
528
+ 'total_volume_usd': total_volume,
529
+ 'transaction_count': alert_count,
530
+ 'netflow_analysis': {
531
+ 'inflow_to_exchanges': exchange_inflow,
532
+ 'outflow_from_exchanges': exchange_outflow,
533
+ 'net_flow': net_exchange_flow,
534
+ 'flow_direction': 'TO_EXCHANGES' if net_exchange_flow < 0 else 'FROM_EXCHANGES',
535
+ 'market_impact': market_impact
536
+ },
537
+ 'recent_alerts': all_alerts[:10],
538
+ 'latest_activity': latest_alert['human_time'] if latest_alert else None,
539
+ 'trading_signals': all_signals,
540
+ 'critical_signals_count': len(critical_signals),
541
+ 'address_classification_stats': {
542
+ 'total_classified': len(self.address_labels),
543
+ 'exchange_addresses': len(self.address_categories['cex']),
544
+ 'whale_addresses': len(self.address_categories['whale']),
545
+ 'unknown_addresses': len(self.address_categories['unknown'])
546
+ },
547
+ 'data_quality': 'HIGH',
548
+ 'networks_scanned': successful_networks
549
+ }
550
+
551
+ except Exception as e:
552
+ print(f"❌ فشل مراقبة الحيتان العامة: {e}")
553
+ return {
554
+ 'data_available': False,
555
+ 'description': f'غير متوفر - فشل في مراقبة الحيتان: {str(e)}',
556
+ 'critical_alert': False,
557
+ 'sentiment': 'UNKNOWN',
558
+ 'total_volume_usd': 0,
559
+ 'transaction_count': 0,
560
+ 'data_quality': 'LOW',
561
+ 'error': str(e),
562
+ 'trading_signals': []
563
+ }
564
 
565
  async def _get_native_coin_price(self, network):
566
+ """جلب الأسعار من مصادر حقيقية"""
567
  now = time.time()
568
  cache_key = f"{network}_price"
569
 
 
570
  if cache_key in self.price_cache and (now - self.price_cache[cache_key]['timestamp']) < 300:
571
  return self.price_cache[cache_key]['price']
572
 
 
575
  return await self._get_price_from_coingecko_fallback(network)
576
 
577
  try:
 
578
  price = await self._get_price_from_kucoin(symbol)
579
  if price and price > 0:
580
  self.price_cache[cache_key] = {'price': price, 'timestamp': now, 'source': 'kucoin'}
581
  return price
582
 
 
583
  price = await self._get_price_from_coingecko_fallback(network)
584
  if price and price > 0:
585
  self.price_cache[cache_key] = {'price': price, 'timestamp': now, 'source': 'coingecko'}
 
592
  return None
593
 
594
  async def _get_price_from_kucoin(self, symbol):
595
+ """جلب السعر من KuCoin"""
596
  try:
 
597
  exchange = ccxt.kucoin({
598
  'sandbox': False,
599
  'enableRateLimit': True
600
  })
601
 
 
602
  trading_symbol = f"{symbol}/USDT"
603
  try:
604
  ticker = await exchange.fetch_ticker(trading_symbol)
 
621
  """الاحتياطي: جلب السعر من CoinGecko"""
622
  coin_map = {
623
  'ethereum': 'ethereum',
624
+ 'bsc': 'binancecoin'
 
 
 
625
  }
626
 
627
  coin_id = coin_map.get(network)
 
643
  return None
644
 
645
  async def _call_rpc_async(self, network, method, params=[]):
646
+ """اتصال RPC غير متزامن"""
647
  max_retries = 2
648
 
649
  for attempt in range(max_retries):
 
653
  return None
654
 
655
  try:
 
656
  if 'infura' in endpoint and self.infura_key:
657
  self._update_api_usage_stats('infura')
658
 
659
  if await self._api_rate_limit_delay('infura'):
660
  continue
 
 
661
 
662
  payload = {"jsonrpc": "2.0", "method": method, "params": params, "id": 1}
663
 
 
667
  response = await client.post(endpoint, json=payload)
668
 
669
  if response.status_code == 401:
670
+ print(f"🔐 خطأ مصادقة في {endpoint}")
671
  self.rpc_failures[network] += 1
672
  continue
673
  elif response.status_code == 429:
674
+ print(f"⏳ Rate limit على {endpoint}")
675
  await asyncio.sleep(2 * (attempt + 1))
676
  continue
677
 
 
683
 
684
  except httpx.HTTPStatusError as e:
685
  if e.response.status_code == 429:
686
+ print(f"⚠️ Rate limit على {endpoint} لـ {network}")
687
  self.rpc_failures[network] += 1
688
  await asyncio.sleep(3 * (attempt + 1))
689
  continue
690
  elif e.response.status_code == 401:
691
+ print(f"🔐 خطأ مصادقة في {endpoint}")
692
  self.rpc_failures[network] += 1
693
  continue
694
  else:
695
+ print(f"⚠️ خطأ HTTP {e.response.status_code} في {endpoint}")
696
  self.rpc_failures[network] += 1
697
 
698
  except Exception as e:
699
+ print(f"⚠️ فشل اتصال RPC لـ {network}: {e}")
700
  self.rpc_failures[network] += 1
701
 
702
  if attempt < max_retries - 1:
 
705
  print(f"❌ فشل جميع محاولات RPC لـ {network}")
706
  return None
707
 
708
+ def _get_next_rpc_endpoint(self, network):
709
+ """الحصول على عنوان RPC التالي"""
710
+ if network not in self.rpc_endpoints:
711
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
712
 
713
+ endpoints = self.rpc_endpoints[network]
714
+ if not endpoints:
715
+ return None
716
+
717
+ index = self.current_rpc_index[network]
718
+ endpoint = endpoints[index]
719
+ self.current_rpc_index[network] = (index + 1) % len(endpoints)
720
+
721
+ return endpoint
722
 
723
+ def _update_api_usage_stats(self, api_name):
724
+ """تحديث إحصائيات استخدام API"""
725
+ now = datetime.now()
726
+ current_date = now.date()
727
 
728
+ stats = self.api_usage_stats[api_name]
729
+
730
+ if current_date != stats['last_reset']:
731
+ stats['requests_today'] = 0
732
+ stats['last_reset'] = current_date
733
+
734
+ current_time = time.time()
735
+ time_diff = current_time - stats['last_request_time']
736
+
737
+ if time_diff < 1.0:
738
+ stats['requests_per_second'] += 1
739
+ else:
740
+ stats['requests_per_second'] = 1
741
+ stats['last_request_time'] = current_time
742
+
743
+ stats['requests_today'] += 1
744
+
745
+ if api_name == 'etherscan':
746
+ if stats['requests_today'] > 90000:
747
+ print(f"🚨 تحذير: طلبات {api_name} اليومية تقترب من الحد")
748
+ if stats['requests_per_second'] > 4:
749
+ print(f"🚨 تحذير: طلبات {api_name} في الثانية تقترب من الحد")
750
+
751
+ elif api_name == 'infura':
752
+ if stats['requests_today'] > 2500000:
753
+ print(f"🚨 تحذير: طلبات {api_name} اليومية تقترب من الحد")
754
+ if stats['requests_per_second'] > 450:
755
+ print(f"🚨 تحذير: طلبات {api_name} في الثانية تقترب من الحد")
756
 
757
+ async def _api_rate_limit_delay(self, api_name):
758
+ """تأخير ذكي لتجنب تجاوز حدود API"""
759
+ stats = self.api_usage_stats[api_name]
760
+
761
+ if api_name == 'etherscan':
762
+ if stats['requests_per_second'] > 4:
763
+ delay = 0.2 * (stats['requests_per_second'] - 4)
764
+ print(f"⏳ تأخير {delay:.2f} ثانية لـ {api_name}")
765
+ await asyncio.sleep(delay)
 
 
 
 
 
 
 
766
 
767
+ if stats['requests_today'] > 95000:
768
+ print(f"🚨 تجاوز الحد اليومي لطلبات {api_name}")
769
+ return True
770
+
771
+ elif api_name == 'infura':
772
+ if stats['requests_per_second'] > 400:
773
+ delay = 0.1 * (stats['requests_per_second'] - 400)
774
+ print(f" تأخير {delay:.2f} ثانية لـ {api_name}")
775
+ await asyncio.sleep(delay)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
776
 
777
+ if stats['requests_today'] > 2800000:
778
+ print(f"🚨 تجاوز الحد اليومي لطلبات {api_name}")
779
+ return True
780
+
781
+ return False
 
 
 
 
 
 
 
782
 
783
  async def get_symbol_specific_whale_data(self, symbol, contract_address=None):
784
  """جلب بيانات الحيتان الخاصة بعملة محددة"""
 
791
  if not contract_address:
792
  return await self._scan_networks_for_symbol(symbol, base_symbol)
793
 
794
+ print(f"🔍 جلب بيانات الحيتان لـ {symbol}")
795
 
796
  api_data = await self._get_combined_api_data(contract_address)
797
 
 
805
  print(f"❌ فشل جلب بيانات الحيتان لـ {symbol}: {e}")
806
  return {
807
  'data_available': False,
808
+ 'description': f'غير متوفر - خطأ في جلب بيانات الحيتان',
809
  'total_volume': 0,
810
  'transfer_count': 0,
811
  'source': 'error'
 
818
  if self.moralis_key:
819
  tasks.append(self._get_moralis_token_data(contract_address))
820
  if self.etherscan_key:
821
+ tasks.append(self._get_etherscan_token_data_v2(contract_address))
822
 
823
  if not tasks:
824
  return []
 
833
  return all_transfers
834
 
835
  async def _get_etherscan_token_data_v2(self, contract_address):
836
+ """جلب بيانات Etherscan"""
837
  if not self.etherscan_key:
838
  return []
839
 
840
  try:
 
841
  self._update_api_usage_stats('etherscan')
842
 
 
843
  if await self._api_rate_limit_delay('etherscan'):
844
  print("⚠️ تجاوز حدود Etherscan، تخطي الطلب")
845
  return []
846
 
 
847
  params = {
848
  "module": "account",
849
  "action": "tokentx",
850
  "contractaddress": contract_address,
851
  "page": 1,
852
+ "offset": 10,
853
  "sort": "desc",
854
  "apikey": self.etherscan_key
855
  }
856
 
 
857
  base_url = "https://api.etherscan.io/api"
858
 
859
+ print(f"🔍 جلب بيانات Etherscan للعقد: {contract_address[:10]}...")
860
 
861
  async with httpx.AsyncClient(timeout=10.0) as client:
862
  response = await client.get(base_url, params=params)
863
 
864
  if response.status_code == 429:
865
+ print("⏳ تجاوز حد معدل Etherscan")
866
  await asyncio.sleep(2)
867
  return []
868
 
869
  response.raise_for_status()
870
  data = response.json()
871
 
 
872
  if data.get('status') == '1' and data.get('message') == 'OK':
873
  result = data.get('result', [])
874
  print(f"✅ بيانات Etherscan: {len(result)} تحويل")
 
894
  response = await self.http_client.get(
895
  f"https://deep-index.moralis.io/api/v2/erc20/{contract_address}/transfers",
896
  headers={"X-API-Key": self.moralis_key},
897
+ params={"chain": "eth", "limit": 10}
898
  )
899
 
900
  if response.status_code == 200:
 
909
  print(f"⚠️ Moralis API error: {e}")
910
  return []
911
 
912
+ async def _enrich_api_data_with_timing(self, api_data):
913
+ """إثراء بيانات API بتوقيتات إضافية"""
914
+ enriched_data = []
915
+ for transfer in api_data:
916
+ try:
917
+ if 'timeStamp' in transfer:
918
+ timestamp = int(transfer['timeStamp'])
919
+ elif 'block_timestamp' in transfer:
920
+ timestamp = int(transfer['block_timestamp'])
921
+ else:
922
+ timestamp = int(time.time())
923
+
924
+ transfer_time = datetime.fromtimestamp(timestamp)
925
+ time_ago = datetime.now() - transfer_time
926
+
927
+ enriched_transfer = {
928
+ **transfer,
929
+ 'human_time': transfer_time.isoformat(),
930
+ 'minutes_ago': time_ago.total_seconds() / 60,
931
+ 'timestamp': timestamp
932
+ }
933
+ enriched_data.append(enriched_transfer)
934
+
935
+ except Exception as e:
936
+ print(f"⚠️ خطأ في إثراء بيانات التحويل: {e}")
937
+ continue
938
+
939
+ return enriched_data
940
+
941
+ def _analyze_symbol_specific_data(self, enriched_data, symbol):
942
+ """تحليل بيانات الرمز المحدد"""
943
+ if not enriched_data:
944
+ return {
945
+ 'data_available': False,
946
+ 'description': f'غير متوفر - لا توجد بيانات تحويل لـ {symbol}',
947
+ 'total_volume': 0,
948
+ 'transfer_count': 0,
949
+ 'source': 'no_data'
950
+ }
951
+
952
+ try:
953
+ volumes = []
954
+ large_transfers = []
955
+
956
+ for transfer in enriched_data:
957
+ value = float(transfer.get('value', 0))
958
+ volumes.append(value)
959
+
960
+ if value > 10000:
961
+ large_transfers.append(transfer)
962
+
963
+ total_volume = sum(volumes)
964
+ transfer_count = len(volumes)
965
+ avg_volume = total_volume / transfer_count if transfer_count > 0 else 0
966
+
967
+ latest_transfer = max(enriched_data, key=lambda x: x['timestamp'])
968
+ oldest_transfer = min(enriched_data, key=lambda x: x['timestamp'])
969
+ time_range_hours = (latest_transfer['timestamp'] - oldest_transfer['timestamp']) / 3600
970
+
971
+ if len(large_transfers) > 5:
972
+ activity_level = 'HIGH'
973
+ description = f"نشاط حيتان مرتفع لـ {symbol}: {len(large_transfers)} تحويل كبير"
974
+ elif len(large_transfers) > 2:
975
+ activity_level = 'MEDIUM'
976
+ description = f"نشاط حيتان متوسط لـ {symbol}: {len(large_transfers)} تحويل كبير"
977
+ else:
978
+ activity_level = 'LOW'
979
+ description = f"نشاط حيتان منخفض لـ {symbol}: {len(large_transfers)} تحويل كبير"
980
+
981
+ return {
982
+ 'data_available': True,
983
+ 'description': description,
984
+ 'total_volume': total_volume,
985
+ 'transfer_count': transfer_count,
986
+ 'average_volume': avg_volume,
987
+ 'large_transfers_count': len(large_transfers),
988
+ 'activity_level': activity_level,
989
+ 'latest_transfer_time': latest_transfer['human_time'],
990
+ 'time_range_hours': time_range_hours,
991
+ 'source': 'api_combined',
992
+ 'recent_large_transfers': large_transfers[:5]
993
+ }
994
+
995
+ except Exception as e:
996
+ print(f"❌ خطأ في تحليل بيانات {symbol}: {e}")
997
+ return {
998
+ 'data_available': False,
999
+ 'description': f'غير متوفر - خطأ في تحليل البيانات',
1000
+ 'total_volume': 0,
1001
+ 'transfer_count': 0,
1002
+ 'source': 'error'
1003
+ }
1004
+
1005
+ async def _find_contract_address(self, symbol):
1006
+ """البحث عن عنوان العقد للرمز المحدد"""
1007
+ symbol_lower = symbol.lower()
1008
+ for key, address in self.contracts_db.items():
1009
+ if symbol_lower in key.lower():
1010
+ return address
1011
+
1012
+ print(f"🔍 لم يتم العثور على عقد لـ {symbol} في قاعدة البيانات")
1013
+ return None
1014
+
1015
+ async def _scan_networks_for_symbol(self, symbol, base_symbol):
1016
+ """مسح الشبكات للعثور على الرمز"""
1017
+ print(f"🔍 مسح الشبكات للعثور على {symbol}...")
1018
+
1019
+ networks_to_scan = ['ethereum', 'bsc']
1020
+
1021
+ for network in networks_to_scan:
1022
+ try:
1023
+ price = await self._get_native_coin_price(network)
1024
+ if price:
1025
+ print(f"✅ تم العثور على {symbol} على شبكة {network} بسعر ${price:.2f}")
1026
+ return {
1027
+ 'data_available': True,
1028
+ 'description': f'تم اكتشاف {symbol} على شبكة {network}',
1029
+ 'network': network,
1030
+ 'price_usd': price,
1031
+ 'source': 'network_scan'
1032
+ }
1033
+ except Exception as e:
1034
+ print(f"⚠️ فشل مسح {network} لـ {symbol}: {e}")
1035
+ continue
1036
+
1037
+ return {
1038
+ 'data_available': False,
1039
+ 'description': f'غير متوفر - لم يتم العثور على {symbol} على أي شبكة',
1040
+ 'source': 'not_found'
1041
+ }
1042
 
1043
  def get_api_usage_stats(self):
1044
  """الحصول على إحصائيات استخدام APIs"""
 
1073
  def __init__(self, contracts_db):
1074
  self.contracts_db = contracts_db or {}
1075
 
 
1076
  try:
1077
  self.exchange = ccxt.kucoin({
1078
  'sandbox': False,
1079
  'enableRateLimit': True
 
1080
  })
1081
  self.exchange.rateLimit = 800
1082
  print("✅ تم تهيئة KuCoin (الوضع العام)")
 
1093
  async def initialize(self):
1094
  self.http_client = httpx.AsyncClient(timeout=20.0)
1095
 
 
1096
  api_status = {
1097
  'KUCOIN': '🟢 عام (بدون مفتاح)',
1098
  'MORALIS_KEY': "🟢 متوفر" if os.getenv('MORALIS_KEY') else "🔴 غير متوفر",
1099
+ 'ETHERSCAN_KEY': "🟢 متوفر" if os.getenv('ETHERSCAN_KEY') else "🔴 غير متوفر",
1100
  'INFURA_KEY': "🟢 متوفر" if os.getenv('INFURA_KEY') else "🔴 غير متوفر"
1101
  }
1102
 
1103
+ print("✅ DataManager initialized - تحليل صافي التدفق المتقدم")
1104
  for key, status in api_status.items():
1105
  print(f" {key}: {status}")
 
 
 
 
 
 
 
 
 
 
 
 
1106
 
1107
  async def close(self):
1108
  if self.http_client:
 
1111
  await self.exchange.close()
1112
 
1113
  async def get_sentiment_safe_async(self):
1114
+ """جلب بيانات المشاعر من مصادر حقيقية"""
1115
  max_retries = 2
1116
  for attempt in range(max_retries):
1117
  try:
 
1139
  return None
1140
 
1141
  async def get_market_context_async(self):
1142
+ """جلب سياق السوق من مصادر حقيقية"""
1143
  max_retries = 2
1144
  for attempt in range(max_retries):
1145
  try:
 
1168
 
1169
  market_trend = self._determine_market_trend(bitcoin_price, sentiment_data, general_whale_activity)
1170
 
1171
+ trading_decision = self._analyze_advanced_trading_signals(general_whale_activity, sentiment_data)
1172
+
1173
  market_context = {
1174
  'timestamp': datetime.now().isoformat(),
1175
  'bitcoin_price_usd': bitcoin_price,
 
1178
  'sentiment_class': sentiment_data.get('feargreed_class') if sentiment_data else 'UNKNOWN',
1179
  'general_whale_activity': general_whale_activity or {
1180
  'data_available': False,
1181
+ 'description': 'غير متوفر - فشل في مراقبة الحيتان',
1182
  'critical_alert': False,
1183
+ 'sentiment': 'UNKNOWN',
1184
+ 'trading_signals': []
1185
  },
1186
  'market_trend': market_trend,
1187
+ 'trading_decision': trading_decision,
1188
  'btc_sentiment': self._get_btc_sentiment(bitcoin_price),
1189
  'data_sources': {
1190
  'prices': bitcoin_price is not None and ethereum_price is not None,
1191
  'sentiment': sentiment_data is not None,
1192
+ 'general_whale_data': general_whale_activity.get('data_available', False) if general_whale_activity else False,
1193
+ 'netflow_analysis': general_whale_activity.get('netflow_analysis', {}).get('market_impact', 'UNKNOWN') if general_whale_activity else 'UNKNOWN'
1194
  },
1195
  'data_quality': 'HIGH',
1196
+ 'risk_assessment': self._assess_market_risk(general_whale_activity, sentiment_data)
1197
  }
1198
 
1199
  print(f"📊 سياق السوق: BTC=${bitcoin_price:,.0f}, ETH=${ethereum_price:,.0f}")
1200
 
1201
+ if general_whale_activity and general_whale_activity.get('netflow_analysis'):
1202
+ netflow = general_whale_activity['netflow_analysis']
1203
+ print(f"📈 تحليل التدفق: صافي ${netflow['net_flow']:,.0f}")
1204
+
1205
+ if general_whale_activity and general_whale_activity.get('trading_signals'):
1206
+ for signal in general_whale_activity['trading_signals']:
1207
+ print(f"🎯 {signal['action']}: {signal['reason']}")
1208
+
1209
  return market_context
1210
 
1211
  except Exception as e:
 
1215
 
1216
  return self._get_minimal_market_context()
1217
 
1218
+ def _analyze_advanced_trading_signals(self, whale_activity, sentiment_data):
1219
+ """تحليل إشارات التداول المتقدمة"""
1220
+ if not whale_activity or not whale_activity.get('data_available'):
1221
+ return {
1222
+ 'action': 'HOLD',
1223
+ 'confidence': 0.0,
1224
+ 'reason': 'غير متوفر - لا توجد بيانات كافية عن الحيتان',
1225
+ 'risk_level': 'UNKNOWN'
1226
+ }
1227
+
1228
+ signals = whale_activity.get('trading_signals', [])
1229
+ netflow_analysis = whale_activity.get('netflow_analysis', {})
1230
+ whale_sentiment = whale_activity.get('sentiment', 'NEUTRAL')
1231
+
1232
+ strongest_signal = None
1233
+ for signal in signals:
1234
+ if not strongest_signal or signal.get('confidence', 0) > strongest_signal.get('confidence', 0):
1235
+ strongest_signal = signal
1236
+
1237
+ if strongest_signal and strongest_signal.get('confidence', 0) > 0.7:
1238
+ action = strongest_signal['action']
1239
+ reason = strongest_signal['reason']
1240
+ confidence = strongest_signal['confidence']
1241
+
1242
+ if 'STRONG_' in action:
1243
+ risk_level = 'HIGH' if 'SELL' in action else 'LOW'
1244
+ else:
1245
+ risk_level = 'MEDIUM' if 'SELL' in action else 'LOW'
1246
+
1247
+ return {
1248
+ 'action': action,
1249
+ 'confidence': confidence,
1250
+ 'reason': reason,
1251
+ 'risk_level': risk_level,
1252
+ 'source': 'netflow_analysis'
1253
+ }
1254
+
1255
+ net_flow = netflow_analysis.get('net_flow', 0)
1256
+ flow_direction = netflow_analysis.get('flow_direction', 'BALANCED')
1257
+
1258
+ if flow_direction == 'TO_EXCHANGES' and abs(net_flow) > 500000:
1259
+ return {
1260
+ 'action': 'SELL',
1261
+ 'confidence': 0.6,
1262
+ 'reason': f'تدفق بيعي إلى المنصات: ${abs(net_flow):,.0f}',
1263
+ 'risk_level': 'MEDIUM',
1264
+ 'source': 'netflow_direction'
1265
+ }
1266
+ elif flow_direction == 'FROM_EXCHANGES' and net_flow > 500000:
1267
+ return {
1268
+ 'action': 'BUY',
1269
+ 'confidence': 0.6,
1270
+ 'reason': f'تراكم شرائي من المنصات: ${net_flow:,.0f}',
1271
+ 'risk_level': 'LOW',
1272
+ 'source': 'netflow_direction'
1273
+ }
1274
+
1275
+ return {
1276
+ 'action': 'HOLD',
1277
+ 'confidence': 0.5,
1278
+ 'reason': f'أنماط التدفق طبيعية - مشاعر الحيتان: {whale_sentiment}',
1279
+ 'risk_level': 'LOW',
1280
+ 'source': 'balanced_flow'
1281
+ }
1282
+
1283
+ def _assess_market_risk(self, whale_activity, sentiment_data):
1284
+ """تقييم مخاطر السوق"""
1285
+ risk_factors = []
1286
+ risk_score = 0
1287
+
1288
+ if whale_activity and whale_activity.get('data_available'):
1289
+ if whale_activity.get('critical_alert', False):
1290
+ risk_factors.append("نشاط حيتان حرج")
1291
+ risk_score += 3
1292
+
1293
+ netflow = whale_activity.get('netflow_analysis', {})
1294
+ if netflow.get('flow_direction') == 'TO_EXCHANGES' and abs(netflow.get('net_flow', 0)) > 1000000:
1295
+ risk_factors.append("تدفق بيعي كبير إلى المنصات")
1296
+ risk_score += 2
1297
+
1298
+ if whale_activity.get('sentiment') == 'BEARISH':
1299
+ risk_factors.append("مشاعر حيتان هبوطية")
1300
+ risk_score += 1
1301
+
1302
+ if sentiment_data and sentiment_data.get('feargreed_value', 50) < 30:
1303
+ risk_factors.append("مخاوف السوق عالية")
1304
+ risk_score += 2
1305
+ elif sentiment_data and sentiment_data.get('feargreed_value', 50) > 70:
1306
+ risk_factors.append("جشع السوق مرتفع")
1307
+ risk_score += 1
1308
+
1309
+ if risk_score >= 4:
1310
+ return {'level': 'HIGH', 'score': risk_score, 'factors': risk_factors}
1311
+ elif risk_score >= 2:
1312
+ return {'level': 'MEDIUM', 'score': risk_score, 'factors': risk_factors}
1313
+ else:
1314
+ return {'level': 'LOW', 'score': risk_score, 'factors': risk_factors}
1315
+
1316
  def _get_btc_sentiment(self, bitcoin_price):
1317
  """تحديد اتجاه البيتكوين"""
1318
  if bitcoin_price is None:
 
1325
  return 'NEUTRAL'
1326
 
1327
  async def _get_prices_with_fallback(self):
1328
+ """جلب الأسعار من مصادر حقيقية"""
1329
  try:
1330
  prices = await self._get_prices_from_kucoin_safe()
1331
  if prices.get('bitcoin') and prices.get('ethereum'):
 
1342
  return {'bitcoin': None, 'ethereum': None}
1343
 
1344
  async def _get_prices_from_kucoin_safe(self):
1345
+ """جلب الأسعار من KuCoin"""
1346
  if not self.exchange:
1347
  return {'bitcoin': None, 'ethereum': None}
1348
 
 
1406
  'timestamp': datetime.now().isoformat(),
1407
  'data_available': False,
1408
  'data_sources': {'prices': False, 'sentiment': False, 'general_whale_data': False},
1409
+ 'error': 'غير متوفر - فشل في جلب بيانات السوق من المصادر الخارجية',
1410
  'market_trend': 'UNKNOWN',
1411
  'btc_sentiment': 'UNKNOWN',
1412
  'data_quality': 'LOW',
1413
  'general_whale_activity': {
1414
  'data_available': False,
1415
+ 'description': 'غير متوفر - فشل في مراقبة الحيتان',
1416
  'critical_alert': False,
1417
  'sentiment': 'UNKNOWN'
1418
  },
 
1420
  'ethereum_price_usd': None,
1421
  'fear_and_greed_index': None,
1422
  'sentiment_class': 'UNKNOWN',
1423
+ 'missing_data': ['غير متوفر - أسعار البيتكوين', 'غير متوفر - أسعار الإيثيريوم', 'غير متوفر - بيانات المشاعر', 'غير متوفر - بيانات الحيتان']
1424
  }
1425
 
1426
  def _determine_market_trend(self, bitcoin_price, sentiment_data, whale_activity):
 
1473
  print(f"⚠️ خطأ في تحديد اتجاه السوق: {e}")
1474
  return "UNKNOWN"
1475
 
 
 
1476
  def get_performance_stats(self):
1477
  """الحصول على إحصائيات الأداء"""
1478
  total_attempts = self.fetch_stats['successful_fetches'] + self.fetch_stats['failed_fetches']
 
1493
 
1494
  return stats
1495
 
1496
+ async def get_symbol_specific_whale_data(self, symbol, contract_address=None):
1497
+ """جلب بيانات الحيتان الخاصة بعملة محددة"""
1498
+ return await self.whale_monitor.get_symbol_specific_whale_data(symbol, contract_address)
1499
+
1500
+ print("✅ Enhanced Data Manager Loaded - جميع البيانات حقيقية")