Riy777 commited on
Commit
81f46a3
·
1 Parent(s): 7b61e7a

Update whale_news_data.py

Browse files
Files changed (1) hide show
  1. whale_news_data.py +999 -542
whale_news_data.py CHANGED
@@ -99,17 +99,21 @@ class EnhancedWhaleMonitor:
99
  self.price_cache = {}
100
  self.last_scan_time = {}
101
 
102
- self.contract_cache = {}
103
- self.symbol_networks = {}
104
 
105
  self.pattern_performance = {}
106
  self.pattern_success_rates = {}
107
 
108
  if self.r2_service:
109
  asyncio.create_task(self._load_pattern_performance())
 
 
 
110
  asyncio.create_task(self._load_contracts_from_r2())
111
 
112
  def _validate_api_keys(self):
 
113
  print("🔍 التحقق من صحة مفاتيح API...")
114
 
115
  if self.moralis_key:
@@ -134,6 +138,7 @@ class EnhancedWhaleMonitor:
134
  print("✅ مفتاح Infura صالح")
135
 
136
  async def _is_valid_infura_key(self):
 
137
  if not self.infura_key:
138
  return False
139
 
@@ -148,11 +153,13 @@ class EnhancedWhaleMonitor:
148
  return False
149
 
150
  async def _load_contracts_from_r2(self):
 
151
  try:
152
  key = "contracts_database.json"
153
  response = self.r2_service.s3_client.get_object(Bucket="trading", Key=key)
154
  contracts_data = json.loads(response['Body'].read())
155
 
 
156
  self.contracts_db.update(contracts_data)
157
  print(f"✅ تم تحميل {len(contracts_data)} عقد من R2")
158
 
@@ -188,6 +195,7 @@ class EnhancedWhaleMonitor:
188
 
189
  async def _load_pattern_performance(self):
190
  if not self.r2_service:
 
191
  return
192
 
193
  try:
@@ -199,9 +207,12 @@ class EnhancedWhaleMonitor:
199
  print(f"✅ تم تحميل بيانات أداء الأنماط: {len(self.pattern_performance)} نمط")
200
  except Exception as e:
201
  print(f"⚠️ لم يتم العثور على بيانات أداء الأنماط: {e}")
 
 
202
 
203
  async def _save_pattern_performance(self):
204
  if not self.r2_service:
 
205
  return
206
 
207
  try:
@@ -217,209 +228,838 @@ class EnhancedWhaleMonitor:
217
  self.r2_service.s3_client.put_object(
218
  Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
219
  )
 
220
  except Exception as e:
221
  print(f"❌ فشل حفظ بيانات أداء الأنماط: {e}")
222
 
223
- async def _find_contract_address_enhanced(self, symbol):
224
- base_symbol = symbol.split("/")[0] if '/' in symbol else symbol
225
- symbol_lower = base_symbol.lower()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
- if symbol_lower in self.contract_cache:
228
- return self.contract_cache[symbol_lower]
229
 
230
- for key, address in self.contracts_db.items():
231
- if symbol_lower in key.lower():
232
- self.contract_cache[symbol_lower] = address
233
- print(f"✅ تم العثور على عنوان العقد لـ {symbol} في قاعدة البيانات: {address}")
234
- return address
235
 
236
- coingecko_address = await self._find_contract_via_coingecko(base_symbol)
237
- if coingecko_address:
238
- self.contract_cache[symbol_lower] = coingecko_address
239
- self.contracts_db[symbol_lower] = coingecko_address
240
- if self.r2_service:
241
- await self._save_contracts_to_r2()
242
- return coingecko_address
243
 
244
- print(f"⚠️ لم يتم العثور على عنوان عقد لـ {symbol} في أي مصدر")
245
- return None
246
 
247
- async def _find_contract_via_coingecko(self, symbol):
 
 
 
 
 
 
 
 
248
  try:
249
- search_url = f"https://api.coingecko.com/api/v3/search?query={symbol}"
250
- async with httpx.AsyncClient(timeout=15) as client:
251
- response = await client.get(search_url)
252
- if response.status_code != 200:
253
- return None
254
-
255
- data = response.json()
256
- coins = data.get('coins', [])
257
-
258
- if not coins:
259
- return None
260
-
261
- best_coin = coins[0]
262
- coin_id = best_coin.get('id')
 
 
 
 
263
 
264
- if not coin_id:
265
- return None
266
 
267
- detail_url = f"https://api.coingecko.com/api/v3/coins/{coin_id}"
268
- detail_response = await client.get(detail_url)
269
- if detail_response.status_code != 200:
270
- return None
271
 
272
- detail_data = detail_response.json()
273
- platforms = detail_data.get('platforms', {})
274
 
275
- for platform in ['ethereum', 'binance-smart-chain', 'polygon-pos']:
276
- if platform in platforms and platforms[platform]:
277
- address = platforms[platform]
278
- if address and len(address) == 42:
279
- print(f"✅ تم العثور على عنوان {symbol} على {platform}: {address}")
280
- self.symbol_networks[symbol] = platform
281
- return address
282
 
283
- return None
284
 
285
  except Exception as e:
286
- print(f"❌ فشل البحث عن عقد {symbol} عبر CoinGecko: {e}")
287
- return None
288
 
289
- async def _save_contracts_to_r2(self):
 
 
 
 
 
 
 
 
 
290
  try:
291
- key = "contracts_database.json"
292
- data_json = json.dumps(self.contracts_db, indent=2).encode('utf-8')
293
- self.r2_service.s3_client.put_object(
294
- Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
295
- )
296
- print(f"✅ تم حفظ {len(self.contracts_db)} عقد في R2")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  except Exception as e:
298
- print(f"❌ فشل حفظ العقود في R2: {e}")
 
299
 
300
- async def get_symbol_specific_whale_data(self, symbol, contract_address=None):
301
  try:
302
- print(f"🔍 جلب بيانات الحيتان لـ {symbol}...")
303
- base_symbol = symbol.split("/")[0] if '/' in symbol else symbol
304
 
305
- if not contract_address:
306
- contract_address = await self._find_contract_address_enhanced(base_symbol)
307
 
308
- if not contract_address:
309
- print(f"⚠️ لم يتم العثور على عنوان عقد لـ {symbol}")
310
- return await self._scan_networks_for_symbol(symbol, base_symbol)
311
 
312
- network = self.symbol_networks.get(base_symbol, 'ethereum')
313
- print(f"🔍 البحث في شبكة {network} للرمز {symbol}")
 
314
 
315
- api_data = await self._get_combined_api_data(contract_address, network)
316
 
317
- if api_data:
318
- enriched_data = await self._enrich_api_data_with_timing(api_data)
319
- result = self._analyze_symbol_specific_data(enriched_data, symbol)
320
- print(f"✅ تم تحليل بيانات الحيتان لـ {symbol}: {result.get('transfer_count', 0)} تحويل")
321
- return result
322
- else:
323
- print(f"⚠️ لا توجد بيانات API لـ {symbol}")
324
- return await self._scan_networks_for_symbol(symbol, base_symbol)
325
-
326
- except Exception as e:
327
- print(f"❌ فشل جلب بيانات الحيتان لـ {symbol}: {e}")
328
- return {
329
- 'data_available': False,
330
- 'description': f'غير متوفر - خطأ في جلب بيانات الحيتان',
331
- 'total_volume': 0,
332
- 'transfer_count': 0,
333
- 'source': 'error'
334
  }
335
-
336
- async def _get_combined_api_data(self, contract_address, network='ethereum'):
337
- tasks = []
338
-
339
- if self.moralis_key and self._is_valid_moralis_key():
340
- tasks.append(self._get_moralis_token_data(contract_address, network))
341
-
342
- if self.etherscan_key and network == 'ethereum':
343
- tasks.append(self._get_etherscan_token_data_v2(contract_address))
344
-
345
- if not tasks:
346
- return await self._get_rpc_token_data(contract_address, network)
347
 
348
- results = await asyncio.gather(*tasks, return_exceptions=True)
349
-
350
- all_transfers = []
351
- for res in results:
352
- if isinstance(res, list):
353
- all_transfers.extend(res)
354
-
355
- if not all_transfers:
356
- return await self._get_rpc_token_data(contract_address, network)
357
-
358
- print(f"✅ تم جمع {len(all_transfers)} تحويل من APIs")
359
- return all_transfers
360
-
361
- def _is_valid_moralis_key(self):
362
- if not self.moralis_key:
363
- return False
364
- return len(self.moralis_key) >= 20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
 
366
- async def _get_rpc_token_data(self, contract_address, network='ethereum'):
 
 
 
367
  try:
368
- print(f"🔍 استخدام RPC لجلب بيانات العقد {contract_address} على شبكة {network}")
369
-
370
- endpoint = self._get_next_rpc_endpoint(network)
371
- if not endpoint:
372
- return []
373
 
374
- transfers = []
 
 
 
 
 
 
 
 
375
 
376
- payload = {"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1}
377
- async with httpx.AsyncClient(timeout=15.0) as client:
378
- response = await client.post(endpoint, json=payload)
379
- if response.status_code != 200:
380
- return []
381
-
382
- result = response.json().get('result')
383
- if not result:
384
- return []
385
-
386
- latest_block = int(result, 16)
387
 
388
- for block_offset in range(50):
389
  block_number = latest_block - block_offset
390
  if block_number < 0:
391
  break
392
 
393
- payload = {"jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": [hex(block_number), True], "id": 1}
394
- async with httpx.AsyncClient(timeout=15.0) as client:
395
- response = await client.post(endpoint, json=payload)
396
- if response.status_code != 200:
397
- continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
 
399
- block_data = response.json().get('result')
400
- if not block_data:
401
- continue
 
 
 
402
 
403
- for tx in block_data.get('transactions', []):
404
- if tx.get('to', '').lower() == contract_address.lower() and tx.get('input', '0x') != '0x':
405
- transfers.append({
406
- 'hash': tx.get('hash'),
407
- 'from': tx.get('from'),
408
- 'to': tx.get('to'),
409
- 'value': str(int(tx.get('value', '0x0'), 16)),
410
- 'timeStamp': str(int(block_data.get('timestamp', '0x0'), 16)),
411
- 'blockNumber': str(block_number)
412
- })
413
-
414
- print(f"✅ تم جمع {len(transfers)} تحويل من RPC")
415
- return transfers
416
-
417
  except Exception as e:
418
- print(f"❌ فشل جلب بيانات RPC: {e}")
419
- return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
 
421
  async def _get_etherscan_token_data_v2(self, contract_address):
422
  if not self.etherscan_key:
 
423
  return []
424
 
425
  try:
@@ -443,11 +1083,14 @@ class EnhancedWhaleMonitor:
443
  response = await client.get(base_url, params=params)
444
 
445
  if response.status_code == 429:
 
446
  await asyncio.sleep(2)
447
  return []
448
  elif response.status_code == 401:
 
449
  return []
450
  elif response.status_code >= 500:
 
451
  return []
452
 
453
  response.raise_for_status()
@@ -458,13 +1101,20 @@ class EnhancedWhaleMonitor:
458
  print(f"✅ تم جلب {len(result)} تحويل من Etherscan")
459
  return result
460
  else:
 
 
461
  return []
462
 
 
 
 
463
  except Exception as e:
 
464
  return []
465
 
466
  async def _get_moralis_token_data(self, contract_address, network='ethereum'):
467
  if not self.moralis_key or not self._is_valid_moralis_key():
 
468
  return []
469
 
470
  try:
@@ -481,6 +1131,7 @@ class EnhancedWhaleMonitor:
481
 
482
  chain_id = chains.get(network)
483
  if not chain_id:
 
484
  return []
485
 
486
  all_transfers = []
@@ -504,18 +1155,23 @@ class EnhancedWhaleMonitor:
504
  all_transfers.extend(result)
505
  print(f"✅ تم جلب {len(result)} تحويل من Moralis ({network})")
506
  elif response.status_code == 401:
 
507
  return []
508
  elif response.status_code == 404:
 
509
  return []
510
  else:
 
511
  return []
512
 
513
- except Exception:
 
514
  return []
515
 
516
  return all_transfers
517
 
518
  except Exception as e:
 
519
  return []
520
 
521
  async def _enrich_api_data_with_timing(self, api_data):
@@ -546,375 +1202,142 @@ class EnhancedWhaleMonitor:
546
  transfer_time = datetime.fromtimestamp(timestamp)
547
  time_ago = datetime.now() - transfer_time
548
 
549
- enriched_transfer = {
550
- **transfer,
551
- 'human_time': transfer_time.isoformat(),
552
- 'minutes_ago': time_ago.total_seconds() / 60,
553
- 'timestamp': timestamp
554
- }
555
- enriched_data.append(enriched_transfer)
556
-
557
- except Exception as e:
558
- continue
559
-
560
- return enriched_data
561
-
562
- def _analyze_symbol_specific_data(self, enriched_data, symbol):
563
- if not enriched_data:
564
- return {
565
- 'data_available': False,
566
- 'description': f'غير متوفر - لا توجد بيانات تحويل لـ {symbol}',
567
- 'total_volume': 0,
568
- 'transfer_count': 0,
569
- 'source': 'no_data'
570
- }
571
-
572
- try:
573
- volumes = []
574
- large_transfers = []
575
-
576
- for transfer in enriched_data:
577
- value = float(transfer.get('value', 0))
578
- if value > 1e15:
579
- value = value / 1e18
580
- volumes.append(value)
581
-
582
- if value > 10000:
583
- large_transfers.append(transfer)
584
-
585
- total_volume = sum(volumes)
586
- transfer_count = len(volumes)
587
- avg_volume = total_volume / transfer_count if transfer_count > 0 else 0
588
-
589
- if transfer_count > 0:
590
- latest_transfer = max(enriched_data, key=lambda x: x['timestamp'])
591
- oldest_transfer = min(enriched_data, key=lambda x: x['timestamp'])
592
- time_range_hours = (latest_transfer['timestamp'] - oldest_transfer['timestamp']) / 3600
593
- else:
594
- time_range_hours = 0
595
-
596
- if len(large_transfers) > 5:
597
- activity_level = 'HIGH'
598
- description = f"نشاط حيتان مرتفع لـ {symbol}: {len(large_transfers)} تحويل كبير"
599
- elif len(large_transfers) > 2:
600
- activity_level = 'MEDIUM'
601
- description = f"نشاط حيتان متوسط لـ {symbol}: {len(large_transfers)} تحويل كبير"
602
- else:
603
- activity_level = 'LOW'
604
- description = f"نشاط حيتان منخفض لـ {symbol}: {len(large_transfers)} تحويل كبير"
605
-
606
- return {
607
- 'data_available': True,
608
- 'description': description,
609
- 'total_volume': total_volume,
610
- 'transfer_count': transfer_count,
611
- 'average_volume': avg_volume,
612
- 'large_transfers_count': len(large_transfers),
613
- 'activity_level': activity_level,
614
- 'latest_transfer_time': latest_transfer['human_time'] if transfer_count > 0 else None,
615
- 'time_range_hours': time_range_hours,
616
- 'source': 'api_combined',
617
- 'recent_large_transfers': large_transfers[:5]
618
- }
619
-
620
- except Exception as e:
621
- print(f"❌ خطأ في تحليل بيانات {symbol}: {e}")
622
- return {
623
- 'data_available': False,
624
- 'description': f'غير متوفر - خطأ في تحليل البيانات',
625
- 'total_volume': 0,
626
- 'transfer_count': 0,
627
- 'source': 'error'
628
- }
629
-
630
- async def _scan_networks_for_symbol(self, symbol, base_symbol):
631
- networks_to_scan = ['ethereum', 'bsc', 'polygon']
632
-
633
- for network in networks_to_scan:
634
- try:
635
- if self.data_manager:
636
- native_price = await self.data_manager.get_native_coin_price(network)
637
- if native_price:
638
- print(f"✅ تم اكتشاف {symbol} على شبكة {network}")
639
- return {
640
- 'data_available': True,
641
- 'description': f'تم اكتشاف {symbol} على شبكة {network}',
642
- 'network': network,
643
- 'native_coin_price': native_price,
644
- 'source': 'network_scan'
645
- }
646
- except Exception as e:
647
- continue
648
-
649
- return {
650
- 'data_available': False,
651
- 'description': f'غير متوفر - لم يتم العثور على {symbol} على أي شبكة',
652
- 'source': 'not_found'
653
- }
654
-
655
- async def _call_rpc_async(self, network, method, params=[]):
656
- max_retries = 3
657
-
658
- for attempt in range(max_retries):
659
- endpoint = self._get_next_rpc_endpoint(network)
660
- if not endpoint:
661
- return None
662
-
663
- try:
664
- payload = {"jsonrpc": "2.0", "method": method, "params": params, "id": 1}
665
- timeout = 25.0 if method == 'eth_getBlockByNumber' else 12.0
666
-
667
- async with httpx.AsyncClient(timeout=timeout) as client:
668
- response = await client.post(endpoint, json=payload)
669
-
670
- if response.status_code in [401, 429]:
671
- continue
672
-
673
- response.raise_for_status()
674
- result = response.json().get('result')
675
-
676
- self.rpc_failures[network] = 0
677
- return result
678
-
679
- except Exception as e:
680
- self.rpc_failures[network] += 1
681
-
682
- if attempt < max_retries - 1:
683
- await asyncio.sleep(1 * (attempt + 1))
684
-
685
- return None
686
-
687
- def _get_next_rpc_endpoint(self, network):
688
- if network not in self.rpc_endpoints:
689
- return None
690
-
691
- endpoints = self.rpc_endpoints[network]
692
- if not endpoints:
693
- return None
694
-
695
- index = self.current_rpc_index[network]
696
- endpoint = endpoints[index]
697
- self.current_rpc_index[network] = (index + 1) % len(endpoints)
698
-
699
- return endpoint
700
-
701
- def _update_api_usage_stats(self, api_name):
702
- now = datetime.now()
703
- current_date = now.date()
704
-
705
- if api_name not in self.api_usage_stats:
706
- self.api_usage_stats[api_name] = {
707
- 'requests_today': 0,
708
- 'requests_per_second': 0,
709
- 'last_request_time': time.time(),
710
- 'last_reset': current_date
711
- }
712
-
713
- stats = self.api_usage_stats[api_name]
714
-
715
- if current_date != stats['last_reset']:
716
- stats['requests_today'] = 0
717
- stats['last_reset'] = current_date
718
-
719
- current_time = time.time()
720
- time_diff = current_time - stats['last_request_time']
721
-
722
- if time_diff < 1.0:
723
- stats['requests_per_second'] += 1
724
- else:
725
- stats['requests_per_second'] = 1
726
- stats['last_request_time'] = current_time
727
-
728
- stats['requests_today'] += 1
729
-
730
- async def _api_rate_limit_delay(self, api_name):
731
- if api_name not in self.api_usage_stats:
732
- return False
733
-
734
- stats = self.api_usage_stats[api_name]
735
-
736
- if api_name == 'etherscan':
737
- if stats['requests_per_second'] > 4:
738
- delay = 0.2 * (stats['requests_per_second'] - 4)
739
- await asyncio.sleep(delay)
740
-
741
- if stats['requests_today'] > 95000:
742
- return True
743
-
744
- elif api_name == 'infura':
745
- if stats['requests_per_second'] > 90:
746
- delay = 0.1 * (stats['requests_per_second'] - 90)
747
- await asyncio.sleep(delay)
748
-
749
- if stats['requests_today'] > 99000:
750
- return True
751
-
752
- elif api_name == 'moralis':
753
- if stats['requests_per_second'] > 4:
754
- delay = 0.2 * (stats['requests_per_second'] - 4)
755
- await asyncio.sleep(delay)
756
-
757
- if stats['requests_today'] > 95000:
758
- return True
759
-
760
- return False
761
-
762
- async def get_general_whale_activity(self):
763
- try:
764
- print("🔍 بدء مسح نشاط الحيتان العام...")
765
- tasks = []
766
- networks_to_scan = ['ethereum', 'bsc']
767
- for network in networks_to_scan:
768
- tasks.append(self._scan_single_evm_network(network))
769
-
770
- results = await asyncio.gather(*tasks, return_exceptions=True)
771
-
772
- all_alerts = []
773
- all_signals = []
774
- successful_networks = 0
775
-
776
- for i, res in enumerate(results):
777
- if isinstance(res, tuple) and len(res) == 2:
778
- alerts, signals = res
779
- all_alerts.extend(alerts)
780
- all_signals.extend(signals)
781
- successful_networks += 1
782
- else:
783
- continue
784
 
785
- all_alerts.sort(key=lambda x: x['timestamp'], reverse=True)
 
 
 
 
 
 
 
 
 
 
 
 
786
 
787
- total_volume = sum(alert['value_usd'] for alert in all_alerts)
788
- alert_count = len(all_alerts)
 
 
 
 
 
 
789
 
790
- exchange_inflow = sum(alert['value_usd'] for alert in all_alerts
791
- if alert['flow_direction'] == 'TO_EXCHANGE')
792
- exchange_outflow = sum(alert['value_usd'] for alert in all_alerts
793
- if alert['flow_direction'] == 'FROM_EXCHANGE')
794
- net_exchange_flow = exchange_inflow - exchange_outflow
795
 
796
- if not all_alerts:
797
- result = {
798
- 'data_available': False,
799
- 'description': 'غير متوفر - لم يتم اكتشاف نشاط حيتان كبير',
800
- 'critical_alert': False,
801
- 'sentiment': 'UNKNOWN',
802
- 'total_volume_usd': 0,
803
- 'transaction_count': 0,
804
- 'networks_scanned': successful_networks,
805
- 'trading_signals': all_signals,
806
- 'netflow_analysis': {
807
- 'inflow_to_exchanges': 0,
808
- 'outflow_from_exchanges': 0,
809
- 'net_flow': 0,
810
- 'flow_direction': 'BALANCED'
811
- }
812
- }
813
- return result
814
 
815
- if net_exchange_flow < -1000000:
816
- sentiment = 'BEARISH'
817
- elif net_exchange_flow < -500000:
818
- sentiment = 'SLIGHTLY_BEARISH'
819
- elif net_exchange_flow > 1000000:
820
- sentiment = 'BULLISH'
821
- elif net_exchange_flow > 500000:
822
- sentiment = 'SLIGHTLY_BULLISH'
823
  else:
824
- sentiment = 'NEUTRAL'
825
-
826
- critical_alert = (
827
- total_volume > 10_000_000 or
828
- any(tx['value_usd'] > 5_000_000 for tx in all_alerts) or
829
- abs(net_exchange_flow) > 5_000_000
830
- )
831
-
832
- result = {
833
- 'data_available': True,
834
- 'description': f"تم اكتشاف {alert_count} معاملة حوت بإجمالي ${total_volume:,.0f}",
835
- 'critical_alert': critical_alert,
836
- 'sentiment': sentiment,
837
- 'total_volume_usd': total_volume,
838
- 'transaction_count': alert_count,
839
- 'netflow_analysis': {
840
- 'inflow_to_exchanges': exchange_inflow,
841
- 'outflow_from_exchanges': exchange_outflow,
842
- 'net_flow': net_exchange_flow,
843
- 'flow_direction': 'TO_EXCHANGES' if net_exchange_flow < 0 else 'FROM_EXCHANGES'
844
- },
845
- 'recent_alerts': all_alerts[:10],
846
- 'trading_signals': all_signals,
847
- 'networks_scanned': successful_networks
848
- }
849
 
850
- return result
 
 
 
 
 
 
 
 
 
 
 
 
851
 
852
  except Exception as e:
853
- print(f"❌ فشل مراقبة الحيتان العامة: {e}")
854
  return {
855
  'data_available': False,
856
- 'description': f'غير متوفر - فشل في مراقبة الحيتان',
857
- 'critical_alert': False,
858
- 'sentiment': 'UNKNOWN',
859
- 'total_volume_usd': 0,
860
- 'transaction_count': 0,
861
- 'trading_signals': []
862
  }
863
 
864
- async def _scan_single_evm_network(self, network):
865
- whale_alerts = []
866
- trading_signals = []
867
 
868
- try:
869
- if not self.data_manager:
870
- return [], []
871
-
872
- price_usd = await self.data_manager.get_native_coin_price(network)
873
- if price_usd is None:
874
- return [], []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
875
 
876
- latest_block_hex = await self._call_rpc_async(network, 'eth_blockNumber')
877
- if not latest_block_hex:
878
- return [], []
879
-
880
- latest_block = int(latest_block_hex, 16)
881
- blocks_to_scan = 10
882
-
883
- for block_offset in range(blocks_to_scan):
884
- block_number = latest_block - block_offset
885
- if block_number < 0:
886
- break
887
-
888
- block_data = await self._call_rpc_async(network, 'eth_getBlockByNumber', [hex(block_number), True])
889
- if not block_data or 'transactions' not in block_data:
890
- continue
891
 
892
- for tx in block_data.get('transactions', []):
893
- value_wei = int(tx.get('value', '0x0'), 16)
894
- if value_wei > 0:
895
- value_native = value_wei / 1e18
896
- value_usd = value_native * price_usd
897
-
898
- if value_usd >= self.whale_threshold_usd:
899
- from_address = tx.get('from', '')
900
- to_address = tx.get('to', '')
901
-
902
- whale_alerts.append({
903
- 'network': network,
904
- 'value_usd': value_usd,
905
- 'from': from_address,
906
- 'to': to_address,
907
- 'hash': tx.get('hash', ''),
908
- 'block_number': block_number,
909
- 'timestamp': int(block_data.get('timestamp', '0x0'), 16)
910
- })
911
-
912
- await asyncio.sleep(0.1)
913
-
914
- return whale_alerts, trading_signals
915
-
916
- except Exception as e:
917
- return [], []
918
 
919
  def _calculate_whale_activity_score(self, whale_data):
920
  if not whale_data.get('data_available', False):
@@ -957,53 +1380,87 @@ class EnhancedWhaleMonitor:
957
  signal.update({
958
  'action': 'STRONG_BUY',
959
  'confidence': min(0.9, whale_score),
960
- 'reason': f'نشاط حيتان قوي: {transfer_count} تحويل بإجمالي ${total_volume:,.0f}'
 
961
  })
962
  elif whale_score > 0.2 and total_volume > 100000:
963
  signal.update({
964
  'action': 'BUY',
965
  'confidence': min(0.7, whale_score),
966
- 'reason': f'نشاط حيتان متوسط: {transfer_count} تحويل بإجمالي ${total_volume:,.0f}'
 
967
  })
968
  else:
969
  signal.update({
970
  'action': 'HOLD',
971
  'confidence': 0.5,
972
- 'reason': f'نشاط حيتان طبيعي: {transfer_count} تحويل'
 
973
  })
974
 
975
  return signal
976
 
977
  except Exception as e:
 
978
  return None
979
 
980
- def get_api_usage_stats(self):
981
- stats = {}
 
982
 
983
- for api_name, api_stats in self.api_usage_stats.items():
984
- if api_name == 'etherscan':
985
- daily_limit = 100000
986
- per_second_limit = 5
987
- elif api_name == 'infura':
988
- daily_limit = 100000
989
- per_second_limit = 100
990
- elif api_name == 'moralis':
991
- daily_limit = 100000
992
- per_second_limit = 5
993
- else:
994
- continue
995
-
996
- stats[api_name] = {
997
- 'requests_today': api_stats['requests_today'],
998
- 'requests_per_second': api_stats['requests_per_second'],
999
- 'daily_limit_remaining': daily_limit - api_stats['requests_today'],
1000
- 'usage_percentage': (api_stats['requests_today'] / daily_limit) * 100,
1001
- 'api_available': getattr(self, f'{api_name}_key') is not None
1002
- }
1003
 
1004
- return stats
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1005
 
1006
  async def cleanup(self):
1007
  await self.http_client.aclose()
 
1008
 
1009
  print("✅ EnhancedWhaleMonitor loaded - Real-time whale activity monitoring ready")
 
99
  self.price_cache = {}
100
  self.last_scan_time = {}
101
 
102
+ self.contract_cache = {} # كاش جديد للعقود
103
+ self.symbol_networks = {} # تتبع الشبكات المناسبة للرموز
104
 
105
  self.pattern_performance = {}
106
  self.pattern_success_rates = {}
107
 
108
  if self.r2_service:
109
  asyncio.create_task(self._load_pattern_performance())
110
+
111
+ # تحميل العقود من R2 إذا كان متوفراً
112
+ if self.r2_service:
113
  asyncio.create_task(self._load_contracts_from_r2())
114
 
115
  def _validate_api_keys(self):
116
+ """التحقق من صحة مفاتيح API"""
117
  print("🔍 التحقق من صحة مفاتيح API...")
118
 
119
  if self.moralis_key:
 
138
  print("✅ مفتاح Infura صالح")
139
 
140
  async def _is_valid_infura_key(self):
141
+ """التحقق من أن مفتاح Infura صالح"""
142
  if not self.infura_key:
143
  return False
144
 
 
153
  return False
154
 
155
  async def _load_contracts_from_r2(self):
156
+ """تحميل قاعدة بيانات العقود من R2"""
157
  try:
158
  key = "contracts_database.json"
159
  response = self.r2_service.s3_client.get_object(Bucket="trading", Key=key)
160
  contracts_data = json.loads(response['Body'].read())
161
 
162
+ # تحديث قاعدة البيانات المحلية
163
  self.contracts_db.update(contracts_data)
164
  print(f"✅ تم تحميل {len(contracts_data)} عقد من R2")
165
 
 
195
 
196
  async def _load_pattern_performance(self):
197
  if not self.r2_service:
198
+ print("⚠️ R2 Service غير متوفر لتحميل بيانات الأنماط")
199
  return
200
 
201
  try:
 
207
  print(f"✅ تم تحميل بيانات أداء الأنماط: {len(self.pattern_performance)} نمط")
208
  except Exception as e:
209
  print(f"⚠️ لم يتم العثور على بيانات أداء الأنماط: {e}")
210
+ self.pattern_performance = {}
211
+ self.pattern_success_rates = {}
212
 
213
  async def _save_pattern_performance(self):
214
  if not self.r2_service:
215
+ print("⚠️ R2 Service غير متوفر لحفظ بيانات الأنماط")
216
  return
217
 
218
  try:
 
228
  self.r2_service.s3_client.put_object(
229
  Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
230
  )
231
+ print(f"✅ تم حفظ بيانات أداء الأنماط: {len(self.pattern_performance)} نمط")
232
  except Exception as e:
233
  print(f"❌ فشل حفظ بيانات أداء الأنماط: {e}")
234
 
235
+ def _classify_address_dynamic(self, address, transaction_history=None):
236
+ try:
237
+ if not isinstance(address, str):
238
+ address = str(address)
239
+
240
+ address_lower = address.lower()
241
+
242
+ if address_lower in self.address_labels:
243
+ return self.address_labels[address_lower]
244
+
245
+ if transaction_history:
246
+ if self._detect_exchange_pattern(transaction_history):
247
+ self.address_labels[address_lower] = 'suspected_cex'
248
+ self.address_categories['cex'].add(address_lower)
249
+ return 'suspected_cex'
250
+
251
+ if self._detect_whale_pattern(transaction_history):
252
+ self.address_labels[address_lower] = 'suspected_whale'
253
+ self.address_categories['whale'].add(address_lower)
254
+ return 'suspected_whale'
255
+
256
+ if self._detect_contract_pattern(transaction_history):
257
+ self.address_labels[address_lower] = 'contract_user'
258
+ self.address_categories['contract'].add(address_lower)
259
+ return 'contract_user'
260
+
261
+ self.address_labels[address_lower] = 'unknown'
262
+ self.address_categories['unknown'].add(address_lower)
263
+ return 'unknown'
264
+ except Exception as e:
265
+ print(f"❌ خطأ في تصنيف العنوان: {e}")
266
+ return 'unknown'
267
+
268
+ def _detect_exchange_pattern(self, transactions):
269
+ if len(transactions) < 10:
270
+ return False
271
 
272
+ unique_senders = set()
273
+ unique_receivers = set()
274
 
275
+ for tx in transactions[-20:]:
276
+ if 'from' in tx:
277
+ unique_senders.add(tx['from'])
278
+ if 'to' in tx:
279
+ unique_receivers.add(tx['to'])
280
 
281
+ if len(unique_senders) > 15 and len(unique_receivers) < 5:
282
+ return True
 
 
 
 
 
283
 
284
+ return False
 
285
 
286
+ def _detect_whale_pattern(self, transactions):
287
+ large_txs = [tx for tx in transactions if tx.get('value_usd', 0) > 100000]
288
+ return len(large_txs) >= 3
289
+
290
+ def _detect_contract_pattern(self, transactions):
291
+ contract_interactions = [tx for tx in transactions if tx.get('to', '') and len(tx.get('to', '')) == 42 and tx.get('input', '0x') != '0x']
292
+ return len(contract_interactions) > len(transactions) * 0.7
293
+
294
+ def _is_exchange_address(self, address):
295
  try:
296
+ if not isinstance(address, str):
297
+ address = str(address)
298
+ address_lower = address.lower()
299
+ return (address_lower in self.address_categories['cex'] or
300
+ address_lower in self.address_categories['exchange'] or
301
+ self.address_labels.get(address_lower) in ['cex', 'suspected_cex'])
302
+ except Exception as e:
303
+ print(f"❌ خطأ في التحقق من عنوان التبادل: {e}")
304
+ return False
305
+
306
+ async def _update_netflow_metrics(self, network, token_symbol, from_address, to_address, value_usd, transaction_hash):
307
+ try:
308
+ from_label = self._classify_address_dynamic(from_address)
309
+ to_label = self._classify_address_dynamic(to_address)
310
+
311
+ if self._is_exchange_address(to_address):
312
+ if token_symbol not in self.netflow_data[network]:
313
+ self._initialize_token_metrics(network, token_symbol)
314
 
315
+ self.netflow_data[network][token_symbol]['inflow'].append(value_usd)
316
+ self.netflow_data[network][token_symbol]['timestamps'].append(datetime.now())
317
 
318
+ if self._is_exchange_address(from_address):
319
+ if token_symbol not in self.netflow_data[network]:
320
+ self._initialize_token_metrics(network, token_symbol)
 
321
 
322
+ self.netflow_data[network][token_symbol]['outflow'].append(value_usd)
323
+ self.netflow_data[network][token_symbol]['timestamps'].append(datetime.now())
324
 
325
+ if token_symbol in self.netflow_data[network]:
326
+ current_inflow = sum(list(self.netflow_data[network][token_symbol]['inflow'])[-12:])
327
+ current_outflow = sum(list(self.netflow_data[network][token_symbol]['outflow'])[-12:])
328
+ current_netflow = current_inflow - current_outflow
 
 
 
329
 
330
+ self.netflow_data[network][token_symbol]['netflow'].append(current_netflow)
331
 
332
  except Exception as e:
333
+ print(f"❌ خطأ في تحديث مقاييس صافي التدفق: {e}")
 
334
 
335
+ def _initialize_token_metrics(self, network, token_symbol):
336
+ self.netflow_data[network][token_symbol] = {
337
+ 'inflow': deque(maxlen=288),
338
+ 'outflow': deque(maxlen=288),
339
+ 'netflow': deque(maxlen=288),
340
+ 'timestamps': deque(maxlen=288),
341
+ 'volume_24h': 0
342
+ }
343
+
344
+ def _calculate_netflow_zscore(self, network, token_symbol, window_hours=24):
345
  try:
346
+ if token_symbol not in self.netflow_data[network]:
347
+ return 0
348
+
349
+ data = self.netflow_data[network][token_symbol]
350
+ netflow_values = list(data['netflow'])
351
+
352
+ if len(netflow_values) < 10:
353
+ return 0
354
+
355
+ window_size = min(len(netflow_values), window_hours * 12)
356
+ recent_values = netflow_values[-window_size:]
357
+
358
+ if len(recent_values) < 5:
359
+ return 0
360
+
361
+ mean_val = np.mean(recent_values)
362
+ std_val = np.std(recent_values)
363
+
364
+ if std_val == 0:
365
+ return 0
366
+
367
+ current_netflow = recent_values[-1] if recent_values else 0
368
+ zscore = (current_netflow - mean_val) / std_val
369
+
370
+ return zscore
371
+
372
  except Exception as e:
373
+ print(f"❌ خطأ في حساب Z-score: {e}")
374
+ return 0
375
 
376
+ def _generate_netflow_signal(self, network, token_symbol):
377
  try:
378
+ if token_symbol not in self.netflow_data[network]:
379
+ return None
380
 
381
+ data = self.netflow_data[network][token_symbol]
382
+ netflow_values = list(data['netflow'])
383
 
384
+ if len(netflow_values) < 12:
385
+ return None
 
386
 
387
+ recent_inflow = sum(list(data['inflow'])[-12:])
388
+ recent_outflow = sum(list(data['outflow'])[-12:])
389
+ recent_netflow = recent_inflow - recent_outflow
390
 
391
+ zscore = self._calculate_netflow_zscore(network, token_symbol)
392
 
393
+ signal = {
394
+ 'symbol': token_symbol,
395
+ 'network': network,
396
+ 'netflow_1h': recent_netflow,
397
+ 'inflow_1h': recent_inflow,
398
+ 'outflow_1h': recent_outflow,
399
+ 'z_score': zscore,
400
+ 'timestamp': datetime.now().isoformat()
 
 
 
 
 
 
 
 
 
401
  }
 
 
 
 
 
 
 
 
 
 
 
 
402
 
403
+ if recent_netflow < -500000 and zscore < -2.5:
404
+ signal.update({
405
+ 'action': 'STRONG_SELL',
406
+ 'confidence': min(0.95, abs(zscore) / 3),
407
+ 'reason': f'تدفق بيعي قوي: ${abs(recent_netflow):,.0f} إلى المنصات',
408
+ 'critical_alert': abs(recent_netflow) > 1000000
409
+ })
410
+ return signal
411
+
412
+ elif recent_netflow < -100000 and zscore < -1.5:
413
+ signal.update({
414
+ 'action': 'SELL',
415
+ 'confidence': min(0.8, abs(zscore) / 2),
416
+ 'reason': f'تدفق بيعي: ${abs(recent_netflow):,.0f} إلى المنصات',
417
+ 'critical_alert': False
418
+ })
419
+ return signal
420
+
421
+ elif recent_netflow > 500000 and zscore > 2.5:
422
+ signal.update({
423
+ 'action': 'STRONG_BUY',
424
+ 'confidence': min(0.95, zscore / 3),
425
+ 'reason': f'تدفق شرائي قوي: ${recent_netflow:,.0f} من المنصات',
426
+ 'critical_alert': recent_netflow > 1000000
427
+ })
428
+ return signal
429
+
430
+ elif recent_netflow > 100000 and zscore > 1.5:
431
+ signal.update({
432
+ 'action': 'BUY',
433
+ 'confidence': min(0.8, zscore / 2),
434
+ 'reason': f'تدفق شرائي: ${recent_netflow:,.0f} من المنصات',
435
+ 'critical_alert': False
436
+ })
437
+ return signal
438
+
439
+ signal.update({
440
+ 'action': 'HOLD',
441
+ 'confidence': 0.5,
442
+ 'reason': f'تدفق متوازن: ${recent_netflow:,.0f}',
443
+ 'critical_alert': False
444
+ })
445
+ return signal
446
+
447
+ except Exception as e:
448
+ print(f"❌ خطأ في توليد إشارة التداول: {e}")
449
+ return None
450
 
451
+ async def _scan_single_evm_network(self, network):
452
+ whale_alerts = []
453
+ trading_signals = []
454
+
455
  try:
456
+ if not self.data_manager:
457
+ print(f"⚠️ DataManager غير متوفر لمسح شبكة {network}")
458
+ return [], []
 
 
459
 
460
+ price_usd = await self.data_manager.get_native_coin_price(network)
461
+ if price_usd is None:
462
+ print(f"⚠️ لم يتم الحصول على سعر لـ {network}")
463
+ return [], []
464
+
465
+ latest_block_hex = await self._call_rpc_async(network, 'eth_blockNumber')
466
+ if not latest_block_hex:
467
+ print(f"⚠️ فشل الحصول على أحدث كتلة لـ {network}")
468
+ return [], []
469
 
470
+ latest_block = int(latest_block_hex, 16)
471
+ blocks_to_scan = 15
472
+ scanned_blocks = 0
473
+ whale_transactions_found = 0
 
 
 
 
 
 
 
474
 
475
+ for block_offset in range(blocks_to_scan):
476
  block_number = latest_block - block_offset
477
  if block_number < 0:
478
  break
479
 
480
+ block_data = await self._call_rpc_async(network, 'eth_getBlockByNumber', [hex(block_number), True])
481
+ if not block_data or 'transactions' not in block_data:
482
+ continue
483
+
484
+ scanned_blocks += 1
485
+
486
+ block_timestamp_hex = block_data.get('timestamp', '0x0')
487
+ block_timestamp = int(block_timestamp_hex, 16)
488
+ block_time = datetime.fromtimestamp(block_timestamp)
489
+ time_ago = datetime.now() - block_time
490
+
491
+ for tx in block_data.get('transactions', []):
492
+ value_wei = int(tx.get('value', '0x0'), 16)
493
+ if value_wei > 0:
494
+ value_native = value_wei / 1e18
495
+ value_usd = value_native * price_usd
496
+
497
+ if value_usd >= self.whale_threshold_usd:
498
+ from_address = tx.get('from', '')
499
+ to_address = tx.get('to', '')
500
+ tx_hash = tx.get('hash', '')
501
+
502
+ await self._update_netflow_metrics(network, 'NATIVE', from_address, to_address, value_usd, tx_hash)
503
+
504
+ from_label = self._classify_address_dynamic(from_address)
505
+ to_label = self._classify_address_dynamic(to_address)
506
+
507
+ whale_alerts.append({
508
+ 'network': network,
509
+ 'value_usd': value_usd,
510
+ 'from': from_address,
511
+ 'to': to_address,
512
+ 'from_label': from_label,
513
+ 'to_label': to_label,
514
+ 'hash': tx_hash,
515
+ 'block_number': block_number,
516
+ 'timestamp': block_timestamp,
517
+ 'human_time': block_time.isoformat(),
518
+ 'minutes_ago': time_ago.total_seconds() / 60,
519
+ 'transaction_type': 'native_transfer',
520
+ 'flow_direction': 'TO_EXCHANGE' if self._is_exchange_address(to_address) else
521
+ 'FROM_EXCHANGE' if self._is_exchange_address(from_address) else 'UNKNOWN'
522
+ })
523
+ whale_transactions_found += 1
524
+
525
+ if block_offset % 3 == 0:
526
+ await asyncio.sleep(0.1)
527
 
528
+ signal = self._generate_netflow_signal(network, 'NATIVE')
529
+ if signal:
530
+ trading_signals.append(signal)
531
+
532
+ print(f"✅ تم مسح {scanned_blocks} كتلة في {network} - تم العثور على {whale_transactions_found} معاملة حوت")
533
+ return whale_alerts, trading_signals
534
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  except Exception as e:
536
+ print(f"❌ خطأ في مسح شبكة {network}: {e}")
537
+ return [], []
538
+
539
+ async def get_general_whale_activity(self):
540
+ try:
541
+ print("🔍 بدء مسح نشاط الحيتان العام...")
542
+ tasks = []
543
+ networks_to_scan = ['ethereum', 'bsc']
544
+ for network in networks_to_scan:
545
+ tasks.append(self._scan_single_evm_network(network))
546
+
547
+ results = await asyncio.gather(*tasks, return_exceptions=True)
548
+
549
+ all_alerts = []
550
+ all_signals = []
551
+ successful_networks = 0
552
+
553
+ for i, res in enumerate(results):
554
+ if isinstance(res, tuple) and len(res) == 2:
555
+ alerts, signals = res
556
+ all_alerts.extend(alerts)
557
+ all_signals.extend(signals)
558
+ successful_networks += 1
559
+ print(f"✅ شبكة {networks_to_scan[i]} تمت معالجتها بنجاح")
560
+ else:
561
+ print(f"❌ فشل معالجة شبكة {networks_to_scan[i]}: {res}")
562
+
563
+ all_alerts.sort(key=lambda x: x['timestamp'], reverse=True)
564
+
565
+ total_volume = sum(alert['value_usd'] for alert in all_alerts)
566
+ alert_count = len(all_alerts)
567
+
568
+ exchange_inflow = sum(alert['value_usd'] for alert in all_alerts
569
+ if alert.get('flow_direction') == 'TO_EXCHANGE')
570
+ exchange_outflow = sum(alert['value_usd'] for alert in all_alerts
571
+ if alert.get('flow_direction') == 'FROM_EXCHANGE')
572
+ net_exchange_flow = exchange_inflow - exchange_outflow
573
+
574
+ critical_signals = [s for s in all_signals if s.get('critical_alert', False)]
575
+
576
+ if not all_alerts:
577
+ result = {
578
+ 'data_available': False,
579
+ 'description': 'غير متوفر - لم يتم اكتشاف نشاط حيتان كبير',
580
+ 'critical_alert': False,
581
+ 'sentiment': 'UNKNOWN',
582
+ 'total_volume_usd': 0,
583
+ 'transaction_count': 0,
584
+ 'data_quality': 'HIGH',
585
+ 'networks_scanned': successful_networks,
586
+ 'trading_signals': all_signals,
587
+ 'netflow_analysis': {
588
+ 'inflow_to_exchanges': 0,
589
+ 'outflow_from_exchanges': 0,
590
+ 'net_flow': 0,
591
+ 'flow_direction': 'BALANCED'
592
+ }
593
+ }
594
+ print("ℹ️ لم يتم اكتشاف أي نشاط حيتان كبير")
595
+ return result
596
+
597
+ latest_alert = all_alerts[0] if all_alerts else None
598
+ latest_time_info = f"آخر نشاط منذ {latest_alert['minutes_ago']:.1f} دقيقة" if latest_alert else ""
599
+
600
+ # تحديد flow_direction بشكل آمن
601
+ flow_direction = 'BALANCED'
602
+ if net_exchange_flow < 0:
603
+ flow_direction = 'TO_EXCHANGES'
604
+ elif net_exchange_flow > 0:
605
+ flow_direction = 'FROM_EXCHANGES'
606
+
607
+ if net_exchange_flow < -1000000:
608
+ sentiment = 'BEARISH'
609
+ flow_description = f"ضغط بيعي قوي: ${abs(net_exchange_flow):,.0f} إلى المنصات"
610
+ market_impact = "HIGH"
611
+ elif net_exchange_flow < -500000:
612
+ sentiment = 'SLIGHTLY_BEARISH'
613
+ flow_description = f"ضغط بيعي: ${abs(net_exchange_flow):,.0f} إلى المنصات"
614
+ market_impact = "MEDIUM"
615
+ elif net_exchange_flow > 1000000:
616
+ sentiment = 'BULLISH'
617
+ flow_description = f"تراكم شرائي قوي: ${net_exchange_flow:,.0f} من المنصات"
618
+ market_impact = "HIGH"
619
+ elif net_exchange_flow > 500000:
620
+ sentiment = 'SLIGHTLY_BULLISH'
621
+ flow_description = f"تراكم شرائي: ${net_exchange_flow:,.0f} من المنصات"
622
+ market_impact = "MEDIUM"
623
+ else:
624
+ sentiment = 'NEUTRAL'
625
+ flow_description = f"تدفق متوازن: ${net_exchange_flow:,.0f} صافي"
626
+ market_impact = "LOW"
627
+
628
+ critical_alert = (
629
+ total_volume > 10_000_000 or
630
+ any(tx['value_usd'] > 5_000_000 for tx in all_alerts) or
631
+ abs(net_exchange_flow) > 5_000_000 or
632
+ len(critical_signals) > 0
633
+ )
634
+
635
+ description = f"تم اكتشاف {alert_count} معاملة حوت بإجمالي ${total_volume:,.0f} عبر {successful_networks} شبكات. {flow_description}. {latest_time_info}"
636
+
637
+ result = {
638
+ 'data_available': True,
639
+ 'description': description,
640
+ 'critical_alert': critical_alert,
641
+ 'sentiment': sentiment,
642
+ 'market_impact': market_impact,
643
+ 'total_volume_usd': total_volume,
644
+ 'transaction_count': alert_count,
645
+ 'netflow_analysis': {
646
+ 'inflow_to_exchanges': exchange_inflow,
647
+ 'outflow_from_exchanges': exchange_outflow,
648
+ 'net_flow': net_exchange_flow,
649
+ 'flow_direction': flow_direction,
650
+ 'market_impact': market_impact
651
+ },
652
+ 'recent_alerts': all_alerts[:10],
653
+ 'latest_activity': latest_alert['human_time'] if latest_alert else None,
654
+ 'trading_signals': all_signals,
655
+ 'critical_signals_count': len(critical_signals),
656
+ 'address_classification_stats': {
657
+ 'total_classified': len(self.address_labels),
658
+ 'exchange_addresses': len(self.address_categories['cex']),
659
+ 'whale_addresses': len(self.address_categories['whale']),
660
+ 'unknown_addresses': len(self.address_categories['unknown'])
661
+ },
662
+ 'data_quality': 'HIGH',
663
+ 'networks_scanned': successful_networks
664
+ }
665
+
666
+ print(f"✅ اكتمل مسح نشاط الحيتان: {alert_count} معاملة، ${total_volume:,.0f} حجم إجمالي")
667
+ return result
668
+
669
+ except Exception as e:
670
+ print(f"❌ فشل مراقبة الحيتان العامة: {e}")
671
+ import traceback
672
+ traceback.print_exc()
673
+ return {
674
+ 'data_available': False,
675
+ 'description': f'غير متوفر - فشل في مراقبة الحيتان: {str(e)}',
676
+ 'critical_alert': False,
677
+ 'sentiment': 'UNKNOWN',
678
+ 'total_volume_usd': 0,
679
+ 'transaction_count': 0,
680
+ 'data_quality': 'LOW',
681
+ 'error': str(e),
682
+ 'trading_signals': []
683
+ }
684
+
685
+ async def _call_rpc_async(self, network, method, params=[]):
686
+ max_retries = 3
687
+
688
+ for attempt in range(max_retries):
689
+ endpoint = self._get_next_rpc_endpoint(network)
690
+ if not endpoint:
691
+ print(f"❌ لا توجد نقاط نهاية متاحة لـ {network}")
692
+ return None
693
+
694
+ try:
695
+ if 'infura' in endpoint and self.infura_key and network == 'ethereum':
696
+ self._update_api_usage_stats('infura')
697
+
698
+ if await self._api_rate_limit_delay('infura'):
699
+ continue
700
+
701
+ payload = {"jsonrpc": "2.0", "method": method, "params": params, "id": 1}
702
+
703
+ timeout = 25.0 if method == 'eth_getBlockByNumber' else 12.0
704
+
705
+ async with httpx.AsyncClient(timeout=timeout) as client:
706
+ response = await client.post(endpoint, json=payload)
707
+
708
+ if response.status_code == 401:
709
+ print(f"❌ خطأ في المصادقة لنقطة النهاية {endpoint}")
710
+ self._remove_rpc_endpoint(network, endpoint)
711
+ continue
712
+ elif response.status_code == 429:
713
+ print(f"⏰ تجاوز حد المعدل لنقطة النهاية {endpoint}")
714
+ await asyncio.sleep(2 * (attempt + 1))
715
+ continue
716
+
717
+ response.raise_for_status()
718
+ result = response.json().get('result')
719
+
720
+ self.rpc_failures[network] = 0
721
+ return result
722
+
723
+ except httpx.HTTPStatusError as e:
724
+ if e.response.status_code == 429:
725
+ self.rpc_failures[network] += 1
726
+ await asyncio.sleep(3 * (attempt + 1))
727
+ continue
728
+ elif e.response.status_code == 401:
729
+ self.rpc_failures[network] += 1
730
+ self._remove_rpc_endpoint(network, endpoint)
731
+ continue
732
+ else:
733
+ self.rpc_failures[network] += 1
734
+ print(f"❌ خطأ HTTP في RPC {network}: {e}")
735
+
736
+ except Exception as e:
737
+ self.rpc_failures[network] += 1
738
+ print(f"❌ خطأ عام في RPC {network}: {e}")
739
+
740
+ if attempt < max_retries - 1:
741
+ await asyncio.sleep(1 * (attempt + 1))
742
+
743
+ print(f"❌ فشل جميع محاولات RPC لـ {network}")
744
+ return None
745
+
746
+ def _get_next_rpc_endpoint(self, network):
747
+ if network not in self.rpc_endpoints:
748
+ return None
749
+
750
+ endpoints = self.rpc_endpoints[network]
751
+ if not endpoints:
752
+ return None
753
+
754
+ index = self.current_rpc_index[network]
755
+ endpoint = endpoints[index]
756
+ self.current_rpc_index[network] = (index + 1) % len(endpoints)
757
+
758
+ return endpoint
759
+
760
+ def _remove_rpc_endpoint(self, network, endpoint):
761
+ if network in self.rpc_endpoints and endpoint in self.rpc_endpoints[network]:
762
+ self.rpc_endpoints[network].remove(endpoint)
763
+ print(f"⚠️ تمت إزالة نقطة RPC {endpoint} من {network} بسبب الفشل")
764
+
765
+ if self.current_rpc_index[network] >= len(self.rpc_endpoints[network]):
766
+ self.current_rpc_index[network] = 0
767
+
768
+ def _update_api_usage_stats(self, api_name):
769
+ now = datetime.now()
770
+ current_date = now.date()
771
+
772
+ if api_name not in self.api_usage_stats:
773
+ self.api_usage_stats[api_name] = {
774
+ 'requests_today': 0,
775
+ 'requests_per_second': 0,
776
+ 'last_request_time': time.time(),
777
+ 'last_reset': current_date
778
+ }
779
+
780
+ stats = self.api_usage_stats[api_name]
781
+
782
+ if current_date != stats['last_reset']:
783
+ stats['requests_today'] = 0
784
+ stats['last_reset'] = current_date
785
+
786
+ current_time = time.time()
787
+ time_diff = current_time - stats['last_request_time']
788
+
789
+ if time_diff < 1.0:
790
+ stats['requests_per_second'] += 1
791
+ else:
792
+ stats['requests_per_second'] = 1
793
+ stats['last_request_time'] = current_time
794
+
795
+ stats['requests_today'] += 1
796
+
797
+ async def _api_rate_limit_delay(self, api_name):
798
+ if api_name not in self.api_usage_stats:
799
+ return False
800
+
801
+ stats = self.api_usage_stats[api_name]
802
+
803
+ if api_name == 'etherscan':
804
+ if stats['requests_per_second'] > 4:
805
+ delay = 0.2 * (stats['requests_per_second'] - 4)
806
+ print(f"⏰ تأخير {delay:.2f} ثانية لتجنب حد المعدل لـ {api_name}")
807
+ await asyncio.sleep(delay)
808
+
809
+ if stats['requests_today'] > 95000:
810
+ print(f"🚨 تجاوز الحد اليومي لـ {api_name}")
811
+ return True
812
+
813
+ elif api_name == 'infura':
814
+ if stats['requests_per_second'] > 90:
815
+ delay = 0.1 * (stats['requests_per_second'] - 90)
816
+ print(f"⏰ تأخير {delay:.2f} ثانية لتجنب حد المعدل لـ {api_name}")
817
+ await asyncio.sleep(delay)
818
+
819
+ if stats['requests_today'] > 99000:
820
+ print(f"🚨 تجاوز الحد اليومي لـ {api_name}")
821
+ return True
822
+
823
+ elif api_name == 'moralis':
824
+ if stats['requests_per_second'] > 4:
825
+ delay = 0.2 * (stats['requests_per_second'] - 4)
826
+ print(f"⏰ تأخير {delay:.2f} ثانية لتجنب حد المعدل لـ {api_name}")
827
+ await asyncio.sleep(delay)
828
+
829
+ if stats['requests_today'] > 95000:
830
+ print(f"🚨 تجاوز الحد اليومي لـ {api_name}")
831
+ return True
832
+
833
+ return False
834
+
835
+ async def _find_contract_address_enhanced(self, symbol):
836
+ """بحث محسن عن عنوان العقد باستخدام مصادر متعددة"""
837
+ base_symbol = symbol.split("/")[0] if '/' in symbol else symbol
838
+ symbol_lower = base_symbol.lower()
839
+
840
+ # البحث في الكاش أولاً
841
+ if symbol_lower in self.contract_cache:
842
+ return self.contract_cache[symbol_lower]
843
+
844
+ # البحث في قاعدة البيانات المحلية
845
+ for key, address in self.contracts_db.items():
846
+ if symbol_lower in key.lower():
847
+ self.contract_cache[symbol_lower] = address
848
+ print(f"✅ تم العثور على عنوان العقد لـ {symbol} في قاعدة البيانات: {address}")
849
+ return address
850
+
851
+ # البحث عبر CoinGecko
852
+ coingecko_address = await self._find_contract_via_coingecko(base_symbol)
853
+ if coingecko_address:
854
+ self.contract_cache[symbol_lower] = coingecko_address
855
+ # حفظ في قاعدة البيانات للمستقبل
856
+ self.contracts_db[symbol_lower] = coingecko_address
857
+ if self.r2_service:
858
+ await self._save_contracts_to_r2()
859
+ return coingecko_address
860
+
861
+ print(f"⚠️ لم يتم العثور على عنوان عقد لـ {symbol} في أي مصدر")
862
+ return None
863
+
864
+ async def _find_contract_via_coingecko(self, symbol):
865
+ """البحث عن عنوان العقد عبر CoinGecko API"""
866
+ try:
867
+ # البحث عن ID العملة أولاً
868
+ search_url = f"https://api.coingecko.com/api/v3/search?query={symbol}"
869
+ async with httpx.AsyncClient(timeout=15) as client:
870
+ response = await client.get(search_url)
871
+ if response.status_code != 200:
872
+ return None
873
+
874
+ data = response.json()
875
+ coins = data.get('coins', [])
876
+
877
+ if not coins:
878
+ return None
879
+
880
+ # أخذ أفضل نتيجة مطابقة
881
+ best_coin = coins[0]
882
+ coin_id = best_coin.get('id')
883
+
884
+ if not coin_id:
885
+ return None
886
+
887
+ # جلب تفاصيل العملة
888
+ detail_url = f"https://api.coingecko.com/api/v3/coins/{coin_id}"
889
+ detail_response = await client.get(detail_url)
890
+ if detail_response.status_code != 200:
891
+ return None
892
+
893
+ detail_data = detail_response.json()
894
+
895
+ # البحث عن عناوين العقود في المنصات المختلفة
896
+ platforms = detail_data.get('platforms', {})
897
+
898
+ # إعطاء الأولوية لـ Ethereum ثم BSC ثم Polygon
899
+ for platform in ['ethereum', 'binance-smart-chain', 'polygon-pos']:
900
+ if platform in platforms and platforms[platform]:
901
+ address = platforms[platform]
902
+ if address and len(address) == 42: # تأكيد أن العنوان صالح
903
+ print(f"✅ تم العثور على عنوان {symbol} على {platform}: {address}")
904
+ # تحديد الشبكة المناسبة للرمز
905
+ network_map = {
906
+ 'ethereum': 'ethereum',
907
+ 'binance-smart-chain': 'bsc',
908
+ 'polygon-pos': 'polygon'
909
+ }
910
+ self.symbol_networks[symbol] = network_map.get(platform, 'ethereum')
911
+ return address
912
+
913
+ return None
914
+
915
+ except Exception as e:
916
+ print(f"❌ فشل البحث عن عقد {symbol} عبر CoinGecko: {e}")
917
+ return None
918
+
919
+ async def _save_contracts_to_r2(self):
920
+ """حفظ قاعدة بيانات العقود إلى R2"""
921
+ try:
922
+ key = "contracts_database.json"
923
+ data_json = json.dumps(self.contracts_db, indent=2).encode('utf-8')
924
+ self.r2_service.s3_client.put_object(
925
+ Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
926
+ )
927
+ print(f"✅ تم حفظ {len(self.contracts_db)} عقد في R2")
928
+ except Exception as e:
929
+ print(f"❌ فشل حفظ العقود في R2: {e}")
930
+
931
+ async def get_symbol_specific_whale_data(self, symbol, contract_address=None):
932
+ try:
933
+ print(f"🔍 جلب بيانات الحيتان لـ {symbol}...")
934
+ base_symbol = symbol.split("/")[0] if '/' in symbol else symbol
935
+
936
+ # البحث عن عنوان العقد إذا لم يتم توفيره
937
+ if not contract_address:
938
+ contract_address = await self._find_contract_address_enhanced(base_symbol)
939
+
940
+ if not contract_address:
941
+ print(f"⚠️ لم يتم العثور على عنوان عقد لـ {symbol}")
942
+ return await self._scan_networks_for_symbol(symbol, base_symbol)
943
+
944
+ # تحديد الشبكة المناسبة للبحث
945
+ network = self.symbol_networks.get(base_symbol, 'ethereum')
946
+ print(f"🔍 البحث في شبكة {network} للرمز {symbol}")
947
+
948
+ api_data = await self._get_combined_api_data(contract_address, network)
949
+
950
+ if api_data:
951
+ enriched_data = await self._enrich_api_data_with_timing(api_data)
952
+ result = self._analyze_symbol_specific_data(enriched_data, symbol)
953
+ print(f"✅ تم تحليل بيانات الحيتان لـ {symbol}: {result.get('transfer_count', 0)} تحويل")
954
+ return result
955
+ else:
956
+ print(f"⚠️ لا توجد بيانات API لـ {symbol}")
957
+ return await self._scan_networks_for_symbol(symbol, base_symbol)
958
+
959
+ except Exception as e:
960
+ print(f"❌ فشل جلب بيانات الحيتان لـ {symbol}: {e}")
961
+ return {
962
+ 'data_available': False,
963
+ 'description': f'غير متوفر - خطأ في جلب بيانات الحيتان',
964
+ 'total_volume': 0,
965
+ 'transfer_count': 0,
966
+ 'source': 'error'
967
+ }
968
+
969
+ async def _get_combined_api_data(self, contract_address, network='ethereum'):
970
+ tasks = []
971
+
972
+ # استخدام APIs المتاحة فقط
973
+ if self.moralis_key and self._is_valid_moralis_key():
974
+ tasks.append(self._get_moralis_token_data(contract_address, network))
975
+
976
+ if self.etherscan_key and network == 'ethereum':
977
+ tasks.append(self._get_etherscan_token_data_v2(contract_address))
978
+
979
+ if not tasks:
980
+ print("⚠️ لا توجد مفاتيح API متاحة لجلب بيانات الرموز")
981
+ return await self._get_rpc_token_data(contract_address, network)
982
+
983
+ results = await asyncio.gather(*tasks, return_exceptions=True)
984
+
985
+ all_transfers = []
986
+ for res in results:
987
+ if isinstance(res, list):
988
+ all_transfers.extend(res)
989
+
990
+ if not all_transfers:
991
+ print("⚠️ فشل جميع APIs، استخدام RPC كبديل")
992
+ return await self._get_rpc_token_data(contract_address, network)
993
+
994
+ print(f"✅ تم جمع {len(all_transfers)} تحويل من APIs")
995
+ return all_transfers
996
+
997
+ def _is_valid_moralis_key(self):
998
+ """التحقق من أن مفتاح Moralis صالح"""
999
+ if not self.moralis_key:
1000
+ return False
1001
+ return len(self.moralis_key) >= 20
1002
+
1003
+ async def _get_rpc_token_data(self, contract_address, network='ethereum'):
1004
+ try:
1005
+ print(f"🔍 استخدام RPC لجلب بيانات العقد {contract_address} على شبكة {network}")
1006
+
1007
+ endpoint = self._get_next_rpc_endpoint(network)
1008
+ if not endpoint:
1009
+ print(f"❌ لا توجد نقاط RPC متاحة لـ {network}")
1010
+ return []
1011
+
1012
+ transfers = []
1013
+
1014
+ payload = {"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": 1}
1015
+ async with httpx.AsyncClient(timeout=15.0) as client:
1016
+ response = await client.post(endpoint, json=payload)
1017
+ if response.status_code != 200:
1018
+ return []
1019
+
1020
+ result = response.json().get('result')
1021
+ if not result:
1022
+ return []
1023
+
1024
+ latest_block = int(result, 16)
1025
+
1026
+ # مسح آخر 50 كتلة
1027
+ for block_offset in range(50):
1028
+ block_number = latest_block - block_offset
1029
+ if block_number < 0:
1030
+ break
1031
+
1032
+ payload = {"jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": [hex(block_number), True], "id": 1}
1033
+ async with httpx.AsyncClient(timeout=15.0) as client:
1034
+ response = await client.post(endpoint, json=payload)
1035
+ if response.status_code != 200:
1036
+ continue
1037
+
1038
+ block_data = response.json().get('result')
1039
+ if not block_data:
1040
+ continue
1041
+
1042
+ for tx in block_data.get('transactions', []):
1043
+ if tx.get('to', '').lower() == contract_address.lower() and tx.get('input', '0x') != '0x':
1044
+ transfers.append({
1045
+ 'hash': tx.get('hash'),
1046
+ 'from': tx.get('from'),
1047
+ 'to': tx.get('to'),
1048
+ 'value': str(int(tx.get('value', '0x0'), 16)),
1049
+ 'timeStamp': str(int(block_data.get('timestamp', '0x0'), 16)),
1050
+ 'blockNumber': str(block_number)
1051
+ })
1052
+
1053
+ print(f"✅ تم جمع {len(transfers)} تحويل من RPC")
1054
+ return transfers
1055
+
1056
+ except Exception as e:
1057
+ print(f"❌ فشل جلب بيانات RPC: {e}")
1058
+ return []
1059
 
1060
  async def _get_etherscan_token_data_v2(self, contract_address):
1061
  if not self.etherscan_key:
1062
+ print("⚠️ مفتاح Etherscan غير متوفر")
1063
  return []
1064
 
1065
  try:
 
1083
  response = await client.get(base_url, params=params)
1084
 
1085
  if response.status_code == 429:
1086
+ print("⏰ تجاوز حد المعدل في Etherscan")
1087
  await asyncio.sleep(2)
1088
  return []
1089
  elif response.status_code == 401:
1090
+ print("❌ خطأ في مصادقة Etherscan - المفتاح غير صالح")
1091
  return []
1092
  elif response.status_code >= 500:
1093
+ print(f"❌ خطأ في خادم Etherscan: {response.status_code}")
1094
  return []
1095
 
1096
  response.raise_for_status()
 
1101
  print(f"✅ تم جلب {len(result)} تحويل من Etherscan")
1102
  return result
1103
  else:
1104
+ error_msg = data.get('result', data.get('message', 'Unknown error'))
1105
+ print(f"⚠️ استجابة غير متوقعة من Etherscan: {error_msg}")
1106
  return []
1107
 
1108
+ except httpx.HTTPStatusError as e:
1109
+ print(f"❌ خطأ HTTP في Etherscan: {e}")
1110
+ return []
1111
  except Exception as e:
1112
+ print(f"❌ خطأ عام في Etherscan: {e}")
1113
  return []
1114
 
1115
  async def _get_moralis_token_data(self, contract_address, network='ethereum'):
1116
  if not self.moralis_key or not self._is_valid_moralis_key():
1117
+ print("⚠️ مفتاح Moralis غير متوفر أو غير صالح")
1118
  return []
1119
 
1120
  try:
 
1131
 
1132
  chain_id = chains.get(network)
1133
  if not chain_id:
1134
+ print(f"⚠️ شبكة {network} غير مدعومة في Moralis")
1135
  return []
1136
 
1137
  all_transfers = []
 
1155
  all_transfers.extend(result)
1156
  print(f"✅ تم جلب {len(result)} تحويل من Moralis ({network})")
1157
  elif response.status_code == 401:
1158
+ print(f"❌ خطأ في مصادقة Moralis - المفتاح غير صالح")
1159
  return []
1160
  elif response.status_code == 404:
1161
+ print(f"⚠️ لم يتم العثور على بيانات للعقد {contract_address} على {network}")
1162
  return []
1163
  else:
1164
+ print(f"⚠️ استجابة غير ناجحة من Moralis لـ {network}: {response.status_code}")
1165
  return []
1166
 
1167
+ except Exception as chain_error:
1168
+ print(f"❌ خطأ في Moralis لـ {network}: {chain_error}")
1169
  return []
1170
 
1171
  return all_transfers
1172
 
1173
  except Exception as e:
1174
+ print(f"❌ خطأ عام في Moralis: {e}")
1175
  return []
1176
 
1177
  async def _enrich_api_data_with_timing(self, api_data):
 
1202
  transfer_time = datetime.fromtimestamp(timestamp)
1203
  time_ago = datetime.now() - transfer_time
1204
 
1205
+ enriched_transfer = {
1206
+ **transfer,
1207
+ 'human_time': transfer_time.isoformat(),
1208
+ 'minutes_ago': time_ago.total_seconds() / 60,
1209
+ 'timestamp': timestamp
1210
+ }
1211
+ enriched_data.append(enriched_transfer)
1212
+
1213
+ except Exception as e:
1214
+ print(f"⚠️ خطأ في توقيت البيانات: {e}")
1215
+ continue
1216
+
1217
+ return enriched_data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1218
 
1219
+ def _analyze_symbol_specific_data(self, enriched_data, symbol):
1220
+ if not enriched_data:
1221
+ return {
1222
+ 'data_available': False,
1223
+ 'description': f'غير متوفر - لا توجد بيانات تحويل لـ {symbol}',
1224
+ 'total_volume': 0,
1225
+ 'transfer_count': 0,
1226
+ 'source': 'no_data'
1227
+ }
1228
+
1229
+ try:
1230
+ volumes = []
1231
+ large_transfers = []
1232
 
1233
+ for transfer in enriched_data:
1234
+ value = float(transfer.get('value', 0))
1235
+ if value > 1e15: # تحويل من wei إلى ether
1236
+ value = value / 1e18
1237
+ volumes.append(value)
1238
+
1239
+ if value > 10000: # تحويل كبير
1240
+ large_transfers.append(transfer)
1241
 
1242
+ total_volume = sum(volumes)
1243
+ transfer_count = len(volumes)
1244
+ avg_volume = total_volume / transfer_count if transfer_count > 0 else 0
 
 
1245
 
1246
+ if transfer_count > 0:
1247
+ latest_transfer = max(enriched_data, key=lambda x: x['timestamp'])
1248
+ oldest_transfer = min(enriched_data, key=lambda x: x['timestamp'])
1249
+ time_range_hours = (latest_transfer['timestamp'] - oldest_transfer['timestamp']) / 3600
1250
+ else:
1251
+ time_range_hours = 0
 
 
 
 
 
 
 
 
 
 
 
 
1252
 
1253
+ if len(large_transfers) > 5:
1254
+ activity_level = 'HIGH'
1255
+ description = f"نشاط حيتان مرتفع لـ {symbol}: {len(large_transfers)} تحويل كبير"
1256
+ elif len(large_transfers) > 2:
1257
+ activity_level = 'MEDIUM'
1258
+ description = f"نشاط حيتان متوسط لـ {symbol}: {len(large_transfers)} تحويل كبير"
 
 
1259
  else:
1260
+ activity_level = 'LOW'
1261
+ description = f"نشاط حيتان منخفض لـ {symbol}: {len(large_transfers)} تحويل كبير"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1262
 
1263
+ return {
1264
+ 'data_available': True,
1265
+ 'description': description,
1266
+ 'total_volume': total_volume,
1267
+ 'transfer_count': transfer_count,
1268
+ 'average_volume': avg_volume,
1269
+ 'large_transfers_count': len(large_transfers),
1270
+ 'activity_level': activity_level,
1271
+ 'latest_transfer_time': latest_transfer['human_time'] if transfer_count > 0 else None,
1272
+ 'time_range_hours': time_range_hours,
1273
+ 'source': 'api_combined',
1274
+ 'recent_large_transfers': large_transfers[:5]
1275
+ }
1276
 
1277
  except Exception as e:
1278
+ print(f"❌ خطأ في تحليل بيانات {symbol}: {e}")
1279
  return {
1280
  'data_available': False,
1281
+ 'description': f'غير متوفر - خطأ في تحليل البيانات',
1282
+ 'total_volume': 0,
1283
+ 'transfer_count': 0,
1284
+ 'source': 'error'
 
 
1285
  }
1286
 
1287
+ async def _scan_networks_for_symbol(self, symbol, base_symbol):
1288
+ networks_to_scan = ['ethereum', 'bsc', 'polygon']
 
1289
 
1290
+ for network in networks_to_scan:
1291
+ try:
1292
+ if self.data_manager:
1293
+ native_price = await self.data_manager.get_native_coin_price(network)
1294
+ if native_price:
1295
+ print(f"✅ تم اكتشاف {symbol} على شبكة {network} - سعر {network.upper()}: ${native_price:.2f}")
1296
+ return {
1297
+ 'data_available': True,
1298
+ 'description': f'تم اكتشاف {symbol} على شبكة {network}',
1299
+ 'network': network,
1300
+ 'native_coin_price': native_price,
1301
+ 'source': 'network_scan'
1302
+ }
1303
+ except Exception as e:
1304
+ print(f"⚠️ فشل مسح شبكة {network} لـ {symbol}: {e}")
1305
+ continue
1306
+
1307
+ print(f"❌ لم يت�� العثور على {symbol} على أي شبكة")
1308
+ return {
1309
+ 'data_available': False,
1310
+ 'description': f'غير متوفر - لم يتم العثور على {symbol} على أي شبكة',
1311
+ 'source': 'not_found'
1312
+ }
1313
 
1314
+ def get_api_usage_stats(self):
1315
+ stats = {}
1316
+
1317
+ for api_name, api_stats in self.api_usage_stats.items():
1318
+ if api_name == 'etherscan':
1319
+ daily_limit = 100000
1320
+ per_second_limit = 5
1321
+ elif api_name == 'infura':
1322
+ daily_limit = 100000
1323
+ per_second_limit = 100
1324
+ elif api_name == 'moralis':
1325
+ daily_limit = 100000
1326
+ per_second_limit = 5
1327
+ else:
1328
+ continue
1329
 
1330
+ stats[api_name] = {
1331
+ 'requests_today': api_stats['requests_today'],
1332
+ 'requests_per_second': api_stats['requests_per_second'],
1333
+ 'daily_limit_remaining': daily_limit - api_stats['requests_today'],
1334
+ 'usage_percentage': (api_stats['requests_today'] / daily_limit) * 100,
1335
+ 'per_second_usage_percentage': (api_stats['requests_per_second'] / per_second_limit) * 100,
1336
+ 'last_reset': api_stats['last_reset'].isoformat(),
1337
+ 'api_available': getattr(self, f'{api_name}_key') is not None
1338
+ }
1339
+
1340
+ return stats
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1341
 
1342
  def _calculate_whale_activity_score(self, whale_data):
1343
  if not whale_data.get('data_available', False):
 
1380
  signal.update({
1381
  'action': 'STRONG_BUY',
1382
  'confidence': min(0.9, whale_score),
1383
+ 'reason': f'نشاط حيتان قوي: {transfer_count} تحويل بإجمالي ${total_volume:,.0f}',
1384
+ 'critical_alert': total_volume > 1000000
1385
  })
1386
  elif whale_score > 0.2 and total_volume > 100000:
1387
  signal.update({
1388
  'action': 'BUY',
1389
  'confidence': min(0.7, whale_score),
1390
+ 'reason': f'نشاط حيتان متوسط: {transfer_count} تحويل بإجمالي ${total_volume:,.0f}',
1391
+ 'critical_alert': False
1392
  })
1393
  else:
1394
  signal.update({
1395
  'action': 'HOLD',
1396
  'confidence': 0.5,
1397
+ 'reason': f'نشاط حيتان طبيعي: {transfer_count} تحويل',
1398
+ 'critical_alert': False
1399
  })
1400
 
1401
  return signal
1402
 
1403
  except Exception as e:
1404
+ print(f"❌ خطأ في توليد إشارة الحيتان: {e}")
1405
  return None
1406
 
1407
+ async def track_pattern_outcome(self, symbol, pattern_analysis, success, profit_percent):
1408
+ if not pattern_analysis:
1409
+ return
1410
 
1411
+ pattern_name = pattern_analysis.get('pattern_detected')
1412
+ confidence = pattern_analysis.get('pattern_confidence', 0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1413
 
1414
+ if pattern_name not in ['no_clear_pattern', 'insufficient_data']:
1415
+ if pattern_name not in self.pattern_performance:
1416
+ self.pattern_performance[pattern_name] = {
1417
+ 'total_trades': 0,
1418
+ 'successful_trades': 0,
1419
+ 'total_profit': 0,
1420
+ 'total_confidence': 0,
1421
+ 'last_updated': datetime.now().isoformat()
1422
+ }
1423
+
1424
+ stats = self.pattern_performance[pattern_name]
1425
+ stats['total_trades'] += 1
1426
+ stats['total_confidence'] += confidence
1427
+ stats['last_updated'] = datetime.now().isoformat()
1428
+
1429
+ if success:
1430
+ stats['successful_trades'] += 1
1431
+ stats['total_profit'] += profit_percent
1432
+
1433
+ if stats['total_trades'] > 0:
1434
+ self.pattern_success_rates[pattern_name] = {
1435
+ 'success_rate': stats['successful_trades'] / stats['total_trades'],
1436
+ 'avg_profit': stats['total_profit'] / stats['total_trades'],
1437
+ 'total_trades': stats['total_trades'],
1438
+ 'last_updated': datetime.now().isoformat()
1439
+ }
1440
+
1441
+ if stats['total_trades'] % 10 == 0:
1442
+ await self._save_pattern_performance()
1443
+
1444
+ print(f"✅ تم تحديث أداء النمط {pattern_name}: {stats['successful_trades']}/{stats['total_trades']} نجاح")
1445
+
1446
+ def get_pattern_reliability(self, pattern_name):
1447
+ if pattern_name in self.pattern_success_rates:
1448
+ return self.pattern_success_rates[pattern_name]['success_rate']
1449
+ return 0.5
1450
+
1451
+ async def save_all_pattern_data(self):
1452
+ await self._save_pattern_performance()
1453
+
1454
+ def get_pattern_performance_summary(self):
1455
+ return {
1456
+ 'pattern_performance': self.pattern_performance,
1457
+ 'pattern_success_rates': self.pattern_success_rates,
1458
+ 'total_patterns_tracked': len(self.pattern_performance),
1459
+ 'last_updated': datetime.now().isoformat()
1460
+ }
1461
 
1462
  async def cleanup(self):
1463
  await self.http_client.aclose()
1464
+ print("✅ تم تنظيف موارد EnhancedWhaleMonitor")
1465
 
1466
  print("✅ EnhancedWhaleMonitor loaded - Real-time whale activity monitoring ready")