Spaces:
Running
Running
| import os | |
| import asyncio | |
| import httpx | |
| import traceback | |
| import time | |
| from datetime import datetime | |
| import ccxt.pro as ccxt | |
| from state import MARKET_STATE_OK | |
| from whale_news_data import whale_monitor_global | |
| from helpers import safe_float_conversion | |
| class DataManager: | |
| def __init__(self, contracts_db): | |
| self.contracts_db = contracts_db or {} | |
| try: | |
| self.exchange = ccxt.kucoin({ | |
| 'sandbox': False, | |
| 'enableRateLimit': True | |
| }) | |
| self.exchange.rateLimit = 800 | |
| print("✅ تم تهيئة KuCoin (الوضع العام)") | |
| except Exception as e: | |
| print(f"⚠️ فشل تهيئة KuCoin: {e}") | |
| self.exchange = None | |
| self._whale_data_cache = {} | |
| self.http_client = None | |
| self.fetch_stats = {'successful_fetches': 0, 'failed_fetches': 0, 'rate_limit_hits': 0} | |
| self.whale_monitor = whale_monitor_global | |
| self.price_cache = {} | |
| async def initialize(self): | |
| self.http_client = httpx.AsyncClient(timeout=20.0) | |
| api_status = { | |
| 'KUCOIN': '🟢 عام (بدون مفتاح)', | |
| 'MORALIS_KEY': "🟢 متوفر" if os.getenv('MORALIS_KEY') else "🔴 غير متوفر", | |
| 'ETHERSCAN_KEY': "🟢 متوفر" if os.getenv('ETHERSCAN_KEY') else "🔴 غير متوفر", | |
| 'INFURA_KEY': "🟢 متوفر" if os.getenv('INFURA_KEY') else "🔴 غير متوفر" | |
| } | |
| print("✅ DataManager initialized - تحليل صافي التدفق المتقدم") | |
| for key, status in api_status.items(): | |
| print(f" {key}: {status}") | |
| async def close(self): | |
| if self.http_client: | |
| await self.http_client.aclose() | |
| if self.exchange: | |
| await self.exchange.close() | |
| async def get_sentiment_safe_async(self): | |
| max_retries = 2 | |
| for attempt in range(max_retries): | |
| try: | |
| async with httpx.AsyncClient(timeout=8) as client: | |
| print(f"🎭 جلب بيانات المشاعر - المحاولة {attempt + 1}/{max_retries}...") | |
| r = await client.get("https://api.alternative.me/fng/") | |
| r.raise_for_status() | |
| data = r.json() | |
| if 'data' not in data or not data['data']: | |
| raise ValueError("بيانات المشاعر غير متوفرة في الاستجابة") | |
| latest_data = data['data'][0] | |
| return { | |
| "feargreed_value": int(latest_data['value']), | |
| "feargreed_class": latest_data['value_classification'], | |
| "source": "alternative.me", | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| except Exception as e: | |
| print(f"⚠️ فشل محاولة {attempt + 1}/{max_retries} لجلب بيانات المشاعر: {e}") | |
| if attempt < max_retries - 1: | |
| await asyncio.sleep(1) | |
| return None | |
| async def get_market_context_async(self): | |
| max_retries = 2 | |
| for attempt in range(max_retries): | |
| try: | |
| print(f"📊 جلب سياق السوق - المحاولة {attempt + 1}/{max_retries}") | |
| sentiment_task = asyncio.wait_for(self.get_sentiment_safe_async(), timeout=10) | |
| price_task = asyncio.wait_for(self._get_prices_with_fallback(), timeout=15) | |
| whale_task = asyncio.wait_for(self.whale_monitor.get_general_whale_activity(), timeout=30) | |
| results = await asyncio.gather(sentiment_task, price_task, whale_task, | |
| return_exceptions=True) | |
| sentiment_data = results[0] if not isinstance(results[0], Exception) else None | |
| price_data = results[1] if not isinstance(results[1], Exception) else {} | |
| general_whale_activity = results[2] if not isinstance(results[2], Exception) else None | |
| bitcoin_price = price_data.get('bitcoin') | |
| ethereum_price = price_data.get('ethereum') | |
| if bitcoin_price is None or ethereum_price is None: | |
| if attempt < max_retries - 1: | |
| await asyncio.sleep(2) | |
| continue | |
| else: | |
| return self._get_minimal_market_context() | |
| market_trend = self._determine_market_trend(bitcoin_price, sentiment_data, general_whale_activity) | |
| trading_decision = self._analyze_advanced_trading_signals(general_whale_activity, sentiment_data) | |
| market_context = { | |
| 'timestamp': datetime.now().isoformat(), | |
| 'bitcoin_price_usd': bitcoin_price, | |
| 'ethereum_price_usd': ethereum_price, | |
| 'fear_and_greed_index': sentiment_data.get('feargreed_value') if sentiment_data else None, | |
| 'sentiment_class': sentiment_data.get('feargreed_class') if sentiment_data else 'UNKNOWN', | |
| 'general_whale_activity': general_whale_activity or { | |
| 'data_available': False, | |
| 'description': 'غير متوفر - فشل في مراقبة الحيتان', | |
| 'critical_alert': False, | |
| 'sentiment': 'UNKNOWN', | |
| 'trading_signals': [] | |
| }, | |
| 'market_trend': market_trend, | |
| 'trading_decision': trading_decision, | |
| 'btc_sentiment': self._get_btc_sentiment(bitcoin_price), | |
| 'data_sources': { | |
| 'prices': bitcoin_price is not None and ethereum_price is not None, | |
| 'sentiment': sentiment_data is not None, | |
| 'general_whale_data': general_whale_activity.get('data_available', False) if general_whale_activity else False, | |
| 'netflow_analysis': general_whale_activity.get('netflow_analysis', {}).get('market_impact', 'UNKNOWN') if general_whale_activity else 'UNKNOWN' | |
| }, | |
| 'data_quality': 'HIGH', | |
| 'risk_assessment': self._assess_market_risk(general_whale_activity, sentiment_data) | |
| } | |
| print(f"📊 سياق السوق: BTC=${bitcoin_price:,.0f}, ETH=${ethereum_price:,.0f}") | |
| if general_whale_activity and general_whale_activity.get('netflow_analysis'): | |
| netflow = general_whale_activity['netflow_analysis'] | |
| print(f"📈 تحليل التدفق: صافي ${netflow['net_flow']:,.0f}") | |
| if general_whale_activity and general_whale_activity.get('trading_signals'): | |
| for signal in general_whale_activity['trading_signals']: | |
| print(f"🎯 {signal['action']}: {signal['reason']}") | |
| return market_context | |
| except Exception as e: | |
| print(f"❌ فشل محاولة {attempt + 1}/{max_retries} لجلب سياق السوق: {e}") | |
| if attempt < max_retries - 1: | |
| await asyncio.sleep(3) | |
| return self._get_minimal_market_context() | |
| def _analyze_advanced_trading_signals(self, whale_activity, sentiment_data): | |
| if not whale_activity or not whale_activity.get('data_available'): | |
| return { | |
| 'action': 'HOLD', | |
| 'confidence': 0.0, | |
| 'reason': 'غير متوفر - لا توجد بيانات كافية عن الحيتان', | |
| 'risk_level': 'UNKNOWN' | |
| } | |
| signals = whale_activity.get('trading_signals', []) | |
| netflow_analysis = whale_activity.get('netflow_analysis', {}) | |
| whale_sentiment = whale_activity.get('sentiment', 'NEUTRAL') | |
| strongest_signal = None | |
| for signal in signals: | |
| if not strongest_signal or signal.get('confidence', 0) > strongest_signal.get('confidence', 0): | |
| strongest_signal = signal | |
| if strongest_signal and strongest_signal.get('confidence', 0) > 0.7: | |
| action = strongest_signal['action'] | |
| reason = strongest_signal['reason'] | |
| confidence = strongest_signal['confidence'] | |
| if 'STRONG_' in action: | |
| risk_level = 'HIGH' if 'SELL' in action else 'LOW' | |
| else: | |
| risk_level = 'MEDIUM' if 'SELL' in action else 'LOW' | |
| return { | |
| 'action': action, | |
| 'confidence': confidence, | |
| 'reason': reason, | |
| 'risk_level': risk_level, | |
| 'source': 'netflow_analysis' | |
| } | |
| net_flow = netflow_analysis.get('net_flow', 0) | |
| flow_direction = netflow_analysis.get('flow_direction', 'BALANCED') | |
| if flow_direction == 'TO_EXCHANGES' and abs(net_flow) > 500000: | |
| return { | |
| 'action': 'SELL', | |
| 'confidence': 0.6, | |
| 'reason': f'تدفق بيعي إلى المنصات: ${abs(net_flow):,.0f}', | |
| 'risk_level': 'MEDIUM', | |
| 'source': 'netflow_direction' | |
| } | |
| elif flow_direction == 'FROM_EXCHANGES' and net_flow > 500000: | |
| return { | |
| 'action': 'BUY', | |
| 'confidence': 0.6, | |
| 'reason': f'تراكم شرائي من المنصات: ${net_flow:,.0f}', | |
| 'risk_level': 'LOW', | |
| 'source': 'netflow_direction' | |
| } | |
| return { | |
| 'action': 'HOLD', | |
| 'confidence': 0.5, | |
| 'reason': f'أنماط التدفق طبيعية - مشاعر الحيتان: {whale_sentiment}', | |
| 'risk_level': 'LOW', | |
| 'source': 'balanced_flow' | |
| } | |
| def _assess_market_risk(self, whale_activity, sentiment_data): | |
| risk_factors = [] | |
| risk_score = 0 | |
| if whale_activity and whale_activity.get('data_available'): | |
| if whale_activity.get('critical_alert', False): | |
| risk_factors.append("نشاط حيتان حرج") | |
| risk_score += 3 | |
| netflow = whale_activity.get('netflow_analysis', {}) | |
| if netflow.get('flow_direction') == 'TO_EXCHANGES' and abs(netflow.get('net_flow', 0)) > 1000000: | |
| risk_factors.append("تدفق بيعي كبير إلى المنصات") | |
| risk_score += 2 | |
| if whale_activity.get('sentiment') == 'BEARISH': | |
| risk_factors.append("مشاعر حيتان هبوطية") | |
| risk_score += 1 | |
| if sentiment_data and sentiment_data.get('feargreed_value', 50) < 30: | |
| risk_factors.append("مخاوف السوق عالية") | |
| risk_score += 2 | |
| elif sentiment_data and sentiment_data.get('feargreed_value', 50) > 70: | |
| risk_factors.append("جشع السوق مرتفع") | |
| risk_score += 1 | |
| if risk_score >= 4: | |
| return {'level': 'HIGH', 'score': risk_score, 'factors': risk_factors} | |
| elif risk_score >= 2: | |
| return {'level': 'MEDIUM', 'score': risk_score, 'factors': risk_factors} | |
| else: | |
| return {'level': 'LOW', 'score': risk_score, 'factors': risk_factors} | |
| def _get_btc_sentiment(self, bitcoin_price): | |
| if bitcoin_price is None: | |
| return 'UNKNOWN' | |
| elif bitcoin_price > 60000: | |
| return 'BULLISH' | |
| elif bitcoin_price < 55000: | |
| return 'BEARISH' | |
| else: | |
| return 'NEUTRAL' | |
| async def _get_prices_with_fallback(self): | |
| try: | |
| prices = await self._get_prices_from_kucoin_safe() | |
| if prices.get('bitcoin') and prices.get('ethereum'): | |
| return prices | |
| prices = await self._get_prices_from_coingecko() | |
| if prices.get('bitcoin') and prices.get('ethereum'): | |
| return prices | |
| return {'bitcoin': None, 'ethereum': None} | |
| except Exception as e: | |
| print(f"❌ فشل جلب الأسعار: {e}") | |
| return {'bitcoin': None, 'ethereum': None} | |
| async def _get_prices_from_kucoin_safe(self): | |
| if not self.exchange: | |
| return {'bitcoin': None, 'ethereum': None} | |
| try: | |
| prices = {'bitcoin': None, 'ethereum': None} | |
| try: | |
| btc_ticker = await self.exchange.fetch_ticker('BTC/USDT') | |
| btc_price = float(btc_ticker.get('last', 0)) if btc_ticker.get('last') else None | |
| if btc_price and btc_price > 0: | |
| prices['bitcoin'] = btc_price | |
| self.price_cache['bitcoin'] = btc_price | |
| print(f"✅ BTC من KuCoin: ${btc_price:.0f}") | |
| except Exception as e: | |
| print(f"⚠️ فشل جلب سعر BTC من KuCoin: {e}") | |
| try: | |
| eth_ticker = await self.exchange.fetch_ticker('ETH/USDT') | |
| eth_price = float(eth_ticker.get('last', 0)) if eth_ticker.get('last') else None | |
| if eth_price and eth_price > 0: | |
| prices['ethereum'] = eth_price | |
| self.price_cache['ethereum'] = eth_price | |
| print(f"✅ ETH من KuCoin: ${eth_price:.0f}") | |
| except Exception as e: | |
| print(f"⚠️ فشل جلب سعر ETH من KuCoin: {e}") | |
| return prices | |
| except Exception as e: | |
| print(f"⚠️ فشل جلب الأسعار من KuCoin: {e}") | |
| return {'bitcoin': None, 'ethereum': None} | |
| async def _get_prices_from_coingecko(self): | |
| try: | |
| await asyncio.sleep(0.5) | |
| url = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd" | |
| async with httpx.AsyncClient() as client: | |
| response = await client.get(url, timeout=10) | |
| response.raise_for_status() | |
| data = response.json() | |
| btc_price = data.get('bitcoin', {}).get('usd') | |
| eth_price = data.get('ethereum', {}).get('usd') | |
| if btc_price and eth_price: | |
| self.price_cache['bitcoin'] = btc_price | |
| self.price_cache['ethereum'] = eth_price | |
| print(f"✅ الأسعار من CoinGecko: BTC=${btc_price:.0f}, ETH=${eth_price:.0f}") | |
| return {'bitcoin': btc_price, 'ethereum': eth_price} | |
| return {'bitcoin': None, 'ethereum': None} | |
| except Exception as e: | |
| print(f"⚠️ فشل جلب الأسعار من CoinGecko: {e}") | |
| return {'bitcoin': None, 'ethereum': None} | |
| def _get_minimal_market_context(self): | |
| return { | |
| 'timestamp': datetime.now().isoformat(), | |
| 'data_available': False, | |
| 'data_sources': {'prices': False, 'sentiment': False, 'general_whale_data': False}, | |
| 'error': 'غير متوفر - فشل في جلب بيانات السوق من المصادر الخارجية', | |
| 'market_trend': 'UNKNOWN', | |
| 'btc_sentiment': 'UNKNOWN', | |
| 'data_quality': 'LOW', | |
| 'general_whale_activity': { | |
| 'data_available': False, | |
| 'description': 'غير متوفر - فشل في مراقبة الحيتان', | |
| 'critical_alert': False, | |
| 'sentiment': 'UNKNOWN' | |
| }, | |
| 'bitcoin_price_usd': None, | |
| 'ethereum_price_usd': None, | |
| 'fear_and_greed_index': None, | |
| 'sentiment_class': 'UNKNOWN', | |
| 'missing_data': ['غير متوفر - أسعار البيتكوين', 'غير متوفر - أسعار الإيثيريوم', 'غير متوفر - بيانات المشاعر', 'غير متوفر - بيانات الحيتان'] | |
| } | |
| def _determine_market_trend(self, bitcoin_price, sentiment_data, whale_activity): | |
| try: | |
| if bitcoin_price is None: | |
| return "UNKNOWN" | |
| score = 0 | |
| data_points = 1 | |
| if bitcoin_price > 60000: | |
| score += 1 | |
| elif bitcoin_price < 55000: | |
| score -= 1 | |
| if sentiment_data and sentiment_data.get('feargreed_value') is not None: | |
| fear_greed = sentiment_data.get('feargreed_value') | |
| if fear_greed > 60: | |
| score += 1 | |
| elif fear_greed < 40: | |
| score -= 1 | |
| data_points += 1 | |
| if (whale_activity and whale_activity.get('data_available') and | |
| whale_activity.get('sentiment') != 'UNKNOWN'): | |
| whale_sentiment = whale_activity.get('sentiment') | |
| if whale_sentiment == 'BULLISH': | |
| score += 1 | |
| elif whale_sentiment == 'BEARISH': | |
| score -= 1 | |
| data_points += 1 | |
| if whale_activity.get('critical_alert', False): | |
| score = -2 | |
| if data_points < 2: | |
| return "UNKNOWN" | |
| if score >= 2: | |
| return "bull_market" | |
| elif score <= -2: | |
| return "bear_market" | |
| elif -1 <= score <= 1: | |
| return "sideways_market" | |
| else: | |
| return "volatile_market" | |
| except Exception as e: | |
| print(f"⚠️ خطأ في تحديد اتجاه السوق: {e}") | |
| return "UNKNOWN" | |
| def get_performance_stats(self): | |
| total_attempts = self.fetch_stats['successful_fetches'] + self.fetch_stats['failed_fetches'] | |
| success_rate = (self.fetch_stats['successful_fetches'] / total_attempts * 100) if total_attempts > 0 else 0 | |
| stats = { | |
| 'total_attempts': total_attempts, | |
| 'successful_fetches': self.fetch_stats['successful_fetches'], | |
| 'failed_fetches': self.fetch_stats['failed_fetches'], | |
| 'rate_limit_hits': self.fetch_stats['rate_limit_hits'], | |
| 'success_rate': f"{success_rate:.1f}%", | |
| 'timestamp': datetime.now().isoformat(), | |
| 'exchange_available': self.exchange is not None | |
| } | |
| api_stats = self.whale_monitor.get_api_usage_stats() | |
| stats['api_usage'] = api_stats | |
| return stats | |
| async def get_symbol_specific_whale_data(self, symbol, contract_address=None): | |
| return await self.whale_monitor.get_symbol_specific_whale_data(symbol, contract_address) | |
| print("✅ Enhanced Data Manager Loaded - جميع البيانات حقيقية") |