Update ml_engine/processor.py
Browse files- ml_engine/processor.py +101 -201
ml_engine/processor.py
CHANGED
|
@@ -18,138 +18,104 @@ class MLProcessor:
|
|
| 18 |
self.data_manager = data_manager
|
| 19 |
self.learning_engine = learning_engine
|
| 20 |
|
| 21 |
-
# يقوم المعالج الرئيسي بتهيئة جميع الأدوات التي يحتاجها
|
| 22 |
self.technical_analyzer = AdvancedTechnicalAnalyzer()
|
| 23 |
self.strategy_engine = MultiStrategyEngine(data_manager, learning_engine)
|
| 24 |
self.monte_carlo_analyzer = MonteCarloAnalyzer()
|
| 25 |
self.pattern_analyzer = ChartPatternAnalyzer()
|
| 26 |
|
| 27 |
-
# 🔴
|
| 28 |
-
#
|
| 29 |
-
# يمكنك تجربة قيمة 1 أو 2 هنا
|
| 30 |
-
self.whale_data_semaphore = asyncio.Semaphore(2)
|
| 31 |
-
# 🔴 --- نهاية الإضافة --- 🔴
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
try:
|
| 36 |
if not raw_data or not raw_data.get('ohlcv'):
|
| 37 |
-
# print(f"❌ بيانات غير صالحة للرمز {raw_data.get('symbol', 'unknown')}") # Reduced logging
|
| 38 |
return None
|
| 39 |
|
| 40 |
symbol = raw_data['symbol']
|
| 41 |
-
# print(f"🔍 معالجة الرمز {symbol} بالتحليلات المتقدمة...") # Reduced logging
|
| 42 |
|
| 43 |
base_analysis = await self.process_and_score_symbol(raw_data)
|
| 44 |
if not base_analysis:
|
| 45 |
return None
|
| 46 |
|
| 47 |
try:
|
|
|
|
| 48 |
advanced_indicators = {}
|
| 49 |
-
ohlcv_available = raw_data.get('ohlcv', {})
|
| 50 |
-
for timeframe, candles in ohlcv_available.items():
|
| 51 |
-
if candles and len(candles) >= 20:
|
| 52 |
dataframe = self._create_dataframe(candles)
|
| 53 |
-
# 1. استخدام محلل المؤشرات (سريع نسبياً)
|
| 54 |
indicators = self.technical_analyzer.calculate_all_indicators(dataframe, timeframe)
|
| 55 |
advanced_indicators[timeframe] = indicators
|
| 56 |
-
|
| 57 |
base_analysis['advanced_indicators'] = advanced_indicators
|
| 58 |
|
| 59 |
-
#
|
| 60 |
-
monte_carlo_probability = await self.monte_carlo_analyzer.predict_1h_probability(ohlcv_available)
|
| 61 |
if monte_carlo_probability is not None:
|
| 62 |
base_analysis['monte_carlo_probability'] = monte_carlo_probability
|
| 63 |
base_analysis['monte_carlo_details'] = self.monte_carlo_analyzer.simulation_results
|
| 64 |
|
| 65 |
-
#
|
| 66 |
-
pattern_analysis = await self.pattern_analyzer.detect_chart_patterns(ohlcv_available)
|
| 67 |
base_analysis['pattern_analysis'] = pattern_analysis
|
| 68 |
|
| 69 |
-
# 🔴 --- تعديل: استخدام
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
# هذه هي العملية التي تستهلك الشبكة بشكل كبير
|
| 76 |
-
whale_data = await self.data_manager.get_whale_data_for_symbol(symbol)
|
| 77 |
-
if whale_data:
|
| 78 |
-
base_analysis['whale_data'] = whale_data
|
| 79 |
-
# print(f" 🐋 [ML-{symbol}] اكتمل جلب بيانات الحيتان.") # Optional debug logging
|
| 80 |
-
except Exception as whale_error:
|
| 81 |
-
print(f" ⚠️ [ML-{symbol}] خطأ أثناء جلب بيانات الحيتان: {whale_error}")
|
| 82 |
-
base_analysis['whale_data'] = {'data_available': False, 'error': str(whale_error)} # Store error info
|
| 83 |
-
# print(f" 🐋 [ML-{symbol}] تحرير قفل بيانات الحيتان.") # Optional debug logging
|
| 84 |
# 🔴 --- نهاية التعديل --- 🔴
|
| 85 |
|
| 86 |
-
#
|
| 87 |
strategy_scores, base_scores = await self.strategy_engine.evaluate_all_strategies(base_analysis, self.market_context)
|
| 88 |
base_analysis['strategy_scores'] = strategy_scores
|
| 89 |
base_analysis['base_strategy_scores'] = base_scores
|
| 90 |
|
| 91 |
-
# (بقية الكود كما هو)
|
| 92 |
if base_scores:
|
|
|
|
| 93 |
best_strategy = max(base_scores.items(), key=lambda x: x[1])
|
| 94 |
best_strategy_name = best_strategy[0]
|
| 95 |
best_strategy_score = best_strategy[1]
|
| 96 |
base_analysis['recommended_strategy'] = best_strategy_name
|
| 97 |
base_analysis['strategy_confidence'] = best_strategy_score
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
base_analysis['target_strategy'] = best_strategy_name
|
| 101 |
-
else:
|
| 102 |
-
base_analysis['target_strategy'] = 'GENERIC'
|
| 103 |
-
|
| 104 |
-
# print(f"🎯 أفضل استراتيجية لـ {symbol}: {best_strategy_name} (ثقة: {best_strategy_score:.2f})") # Reduced logging
|
| 105 |
|
| 106 |
enhanced_score = self._calculate_enhanced_final_score(base_analysis)
|
| 107 |
base_analysis['enhanced_final_score'] = enhanced_score
|
| 108 |
|
| 109 |
-
# Reduced logging for success case
|
| 110 |
-
# print(f"✅ اكتمل التحليل المتقدم لـ {symbol}:")
|
| 111 |
-
# print(f" 📊 النهائي: {enhanced_score:.3f}")
|
| 112 |
-
# if monte_carlo_probability is not None:
|
| 113 |
-
# print(f" 🎯 مونت كارلو: {monte_carlo_probability:.3f}")
|
| 114 |
-
# print(f" 🎯 نمط: {pattern_analysis.get('pattern_detected')} (ثقة: {pattern_analysis.get('pattern_confidence', 0):.2f})")
|
| 115 |
-
# if whale_data and whale_data.get('data_available'):
|
| 116 |
-
# print(f" 🐋 حيتان: {whale_data.get('trading_signal', {}).get('action', 'HOLD')} (ثقة: {whale_data.get('trading_signal', {}).get('confidence', 0):.2f})")
|
| 117 |
-
|
| 118 |
return base_analysis
|
| 119 |
|
| 120 |
except Exception as strategy_error:
|
| 121 |
print(f"❌ خطأ في التحليل المتقدم لـ {symbol}: {strategy_error}")
|
| 122 |
-
# Ensure base_analysis is returned even if advanced steps fail
|
| 123 |
return base_analysis
|
| 124 |
|
| 125 |
except Exception as error:
|
| 126 |
print(f"❌ خطأ فادح في المعالجة المحسنة للرمز {raw_data.get('symbol', 'unknown')}: {error}")
|
| 127 |
-
|
| 128 |
-
|
|
|
|
| 129 |
|
| 130 |
-
# ...(بقية الدوال في الملف كما هي بدون تغيير)...
|
| 131 |
-
|
| 132 |
def _create_dataframe(self, candles):
|
| 133 |
"""إنشاء DataFrame من بيانات الشموع مع DatetimeIndex مرتب"""
|
| 134 |
try:
|
| 135 |
-
|
| 136 |
-
if not candles:
|
| 137 |
-
return pd.DataFrame()
|
| 138 |
df = pd.DataFrame(candles, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
| 139 |
df[['open', 'high', 'low', 'close', 'volume']] = df[['open', 'high', 'low', 'close', 'volume']].astype(float)
|
| 140 |
-
|
| 141 |
-
# تحويل timestamp إلى datetime وضبطه كـ index
|
| 142 |
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
|
| 143 |
df.set_index('timestamp', inplace=True)
|
| 144 |
-
|
| 145 |
-
# التأكد من أن الفهرس مرتب
|
| 146 |
df.sort_index(inplace=True)
|
| 147 |
-
|
| 148 |
return df
|
| 149 |
except Exception as e:
|
| 150 |
print(f"❌ خطأ في إنشاء DataFrame: {e}")
|
| 151 |
return pd.DataFrame()
|
| 152 |
-
|
| 153 |
def _calculate_enhanced_final_score(self, analysis):
|
| 154 |
"""حساب الدرجة النهائية المحسنة"""
|
| 155 |
try:
|
|
@@ -157,198 +123,132 @@ class MLProcessor:
|
|
| 157 |
monte_carlo_score = analysis.get('monte_carlo_probability', 0)
|
| 158 |
pattern_confidence = analysis.get('pattern_analysis', {}).get('pattern_confidence', 0)
|
| 159 |
strategy_confidence = analysis.get('strategy_confidence', 0)
|
| 160 |
-
# 🔴 إضافة: عامل لبيانات الحيتان (إذا كانت متاحة وذات ثقة)
|
| 161 |
whale_confidence = 0
|
| 162 |
whale_data = analysis.get('whale_data')
|
| 163 |
if whale_data and whale_data.get('data_available'):
|
| 164 |
signal = whale_data.get('trading_signal', {})
|
| 165 |
-
# نعطي وزناً فقط للإشارات غير المحايدة وذات الثقة المتوسطة فأعلى
|
| 166 |
if signal.get('action') != 'HOLD' and signal.get('confidence', 0) >= 0.5:
|
| 167 |
whale_confidence = signal.get('confidence', 0)
|
| 168 |
|
| 169 |
-
|
| 170 |
components = []
|
| 171 |
weights = []
|
| 172 |
|
| 173 |
-
if base_score > 0:
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
if
|
| 178 |
-
components.append(monte_carlo_score)
|
| 179 |
-
weights.append(0.25) # تقليل الوزن قليلاً
|
| 180 |
-
|
| 181 |
-
if pattern_confidence > 0:
|
| 182 |
-
components.append(pattern_confidence)
|
| 183 |
-
weights.append(0.25) # الحفاظ على الوزن
|
| 184 |
-
|
| 185 |
-
if strategy_confidence > 0:
|
| 186 |
-
components.append(strategy_confidence)
|
| 187 |
-
weights.append(0.15) # تقليل الوزن قليلاً
|
| 188 |
|
| 189 |
-
|
| 190 |
-
if whale_confidence > 0:
|
| 191 |
-
components.append(whale_confidence)
|
| 192 |
-
weights.append(0.15) # إعطاء وزن معقول لبيانات الحيتان
|
| 193 |
-
|
| 194 |
-
if not components:
|
| 195 |
-
return 0 # ❌ لا توجد بيانات صالحة
|
| 196 |
|
| 197 |
-
# حساب المتوسط المرجح
|
| 198 |
total_weight = sum(weights)
|
| 199 |
-
if total_weight == 0:
|
| 200 |
-
return 0
|
| 201 |
|
| 202 |
enhanced_score = sum(comp * weight for comp, weight in zip(components, weights)) / total_weight
|
| 203 |
|
| 204 |
-
return min(max(enhanced_score, 0.0), 1.0)
|
| 205 |
|
| 206 |
except Exception as e:
|
| 207 |
print(f"❌ خطأ في حساب الدرجة المحسنة: {e}")
|
| 208 |
-
return analysis.get('final_score', 0)
|
| 209 |
-
|
| 210 |
async def process_and_score_symbol(self, raw_data):
|
| 211 |
"""المعالجة الأساسية للرمز"""
|
| 212 |
try:
|
| 213 |
symbol = raw_data['symbol']
|
| 214 |
-
ohlcv_data = raw_data.get('ohlcv')
|
| 215 |
|
| 216 |
-
|
| 217 |
-
if not ohlcv_data:
|
| 218 |
-
# print(f"⚠️ بيانات OHLCV مفقودة للمعالجة الأساسية لـ {symbol}") # Reduced logging
|
| 219 |
-
return None
|
| 220 |
|
| 221 |
current_price = raw_data.get('current_price', 0)
|
| 222 |
layer1_score = raw_data.get('layer1_score', 0)
|
| 223 |
reasons = raw_data.get('reasons_for_candidacy', [])
|
| 224 |
-
|
| 225 |
final_score = layer1_score
|
| 226 |
|
| 227 |
return {
|
| 228 |
-
'symbol': symbol,
|
| 229 |
-
'
|
| 230 |
-
'
|
| 231 |
-
'enhanced_final_score': final_score, # Initialize enhanced score
|
| 232 |
-
'reasons_for_candidacy': reasons,
|
| 233 |
-
'layer1_score': layer1_score,
|
| 234 |
-
# 🔴 إضافة: التأكد من تمرير بيانات OHLCV هنا أيضاً
|
| 235 |
-
'ohlcv': ohlcv_data
|
| 236 |
}
|
| 237 |
-
|
| 238 |
except Exception as error:
|
| 239 |
print(f"❌ خطأ في المعالجة الأساسية للرمز {raw_data.get('symbol', 'unknown')}: {error}")
|
| 240 |
return None
|
| 241 |
-
|
| 242 |
def filter_top_candidates(self, candidates, number_of_candidates=10):
|
| 243 |
"""تصفية أفضل المرشحين"""
|
| 244 |
-
valid_candidates = [
|
| 245 |
-
|
| 246 |
-
if not valid_candidates:
|
| 247 |
-
print("❌ لا توجد مرشحات صالحة للتصفية")
|
| 248 |
-
return []
|
| 249 |
-
|
| 250 |
-
# Sort by 'enhanced_final_score', safely handling missing keys
|
| 251 |
-
sorted_candidates = sorted(valid_candidates,
|
| 252 |
-
key=lambda candidate: candidate.get('enhanced_final_score', 0),
|
| 253 |
-
reverse=True)
|
| 254 |
|
|
|
|
| 255 |
top_candidates = sorted_candidates[:number_of_candidates]
|
| 256 |
|
| 257 |
print(f"🎖️ أفضل {len(top_candidates)} مرشح:")
|
| 258 |
-
for i,
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
symbol
|
| 266 |
-
|
| 267 |
-
print(f" {i+1}. {symbol}:")
|
| 268 |
-
print(f" 📊 النهائي: {score:.3f}")
|
| 269 |
-
if mc_score is not None: # Check if mc_score exists and is not None
|
| 270 |
-
print(f" 🎯 مونت كارلو: {mc_score:.3f}")
|
| 271 |
print(f" 🎯 استراتيجية: {strategy} | نمط: {pattern}")
|
| 272 |
-
|
| 273 |
-
whale_data = candidate.get('whale_data')
|
| 274 |
if whale_data and whale_data.get('data_available'):
|
| 275 |
signal = whale_data.get('trading_signal', {})
|
| 276 |
-
|
| 277 |
-
whale_conf = signal.get('confidence', 0)
|
| 278 |
-
print(f" 🐋 حيتان: {whale_action} (ثقة: {whale_conf:.2f})")
|
| 279 |
-
|
| 280 |
return top_candidates
|
| 281 |
|
| 282 |
-
# (safe_json_parse و process_multiple_symbols_parallel كما
|
| 283 |
def safe_json_parse(json_string):
|
| 284 |
-
|
| 285 |
-
# Added check for None or empty string
|
| 286 |
-
if not json_string:
|
| 287 |
-
return None
|
| 288 |
try:
|
| 289 |
return json.loads(json_string)
|
| 290 |
except json.JSONDecodeError as e:
|
| 291 |
try:
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
except json.JSONDecodeError:
|
| 307 |
-
# print(f"❌ فشل تحليل JSON بعد الإصلاح: {e} - String: {json_string[:200]}...") # Reduced logging
|
| 308 |
-
return None
|
| 309 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
|
| 311 |
-
async def process_multiple_symbols_parallel(self, symbols_data_list, max_concurrent=20):
|
| 312 |
-
"""معالجة متعددة للرموز بشكل متوازي مع التحكم في التزامن"""
|
| 313 |
try:
|
| 314 |
-
print(f"🚀 بدء المعالجة المتوازية لـ {len(symbols_data_list)} رمز (بحد أقصى {max_concurrent}
|
| 315 |
|
| 316 |
-
|
| 317 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
for batch_num, batch in enumerate(batches):
|
| 322 |
-
print(f" 🔄 معالجة الدفعة {batch_num + 1}/{len(batches)} ({len(batch)} رمز)...")
|
| 323 |
-
|
| 324 |
-
batch_tasks = []
|
| 325 |
-
for symbol_data in batch:
|
| 326 |
-
# 🔴 استخدام المهمة المعدلة مع منظم سرعة الحيتان
|
| 327 |
-
task = asyncio.create_task(self.process_and_score_symbol_enhanced(symbol_data))
|
| 328 |
-
batch_tasks.append(task)
|
| 329 |
-
|
| 330 |
-
batch_results = await asyncio.gather(*batch_tasks, return_exceptions=True)
|
| 331 |
-
|
| 332 |
-
successful_results = []
|
| 333 |
-
for result in batch_results:
|
| 334 |
-
if isinstance(result, Exception):
|
| 335 |
-
# print(f" ⚠️ خطأ في مهمة متوازية: {result}") # Reduced logging
|
| 336 |
-
continue
|
| 337 |
-
# Check result is a dict and has the score key
|
| 338 |
-
if isinstance(result, dict) and result.get('enhanced_final_score', 0) > 0.4:
|
| 339 |
-
successful_results.append(result)
|
| 340 |
-
|
| 341 |
-
all_results.extend(successful_results)
|
| 342 |
-
print(f" ✅ اكتملت الدفعة {batch_num + 1}: {len(successful_results)}/{len(batch)} ناجحة")
|
| 343 |
-
|
| 344 |
-
if batch_num < len(batches) - 1:
|
| 345 |
-
await asyncio.sleep(1)
|
| 346 |
-
|
| 347 |
-
print(f"🎯 اكتملت المعالجة المتوازية: {len(all_results)}/{len(symbols_data_list)} رمز تم تحليلها بنجاح")
|
| 348 |
-
return all_results
|
| 349 |
|
| 350 |
except Exception as error:
|
| 351 |
-
print(f"❌ خطأ في المعالجة
|
| 352 |
return []
|
| 353 |
|
| 354 |
-
|
|
|
|
|
|
| 18 |
self.data_manager = data_manager
|
| 19 |
self.learning_engine = learning_engine
|
| 20 |
|
|
|
|
| 21 |
self.technical_analyzer = AdvancedTechnicalAnalyzer()
|
| 22 |
self.strategy_engine = MultiStrategyEngine(data_manager, learning_engine)
|
| 23 |
self.monte_carlo_analyzer = MonteCarloAnalyzer()
|
| 24 |
self.pattern_analyzer = ChartPatternAnalyzer()
|
| 25 |
|
| 26 |
+
# 🔴 تم إزالة منظم سرعة الحيتان من هنا
|
| 27 |
+
# self.whale_data_semaphore = asyncio.Semaphore(1)
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
+
# 🔴 --- تعديل جوهري: إضافة معلمة جديدة واستخدامها --- 🔴
|
| 30 |
+
async def process_and_score_symbol_enhanced(self, raw_data, preloaded_whale_data: dict = None):
|
| 31 |
+
"""
|
| 32 |
+
(معدلة) المعالجة المحسنة للرموز
|
| 33 |
+
- تتوقع بيانات الحيتان كمعلمة (preloaded_whale_data)
|
| 34 |
+
- لا تقوم بجلب بيانات الحيتان بنفسها
|
| 35 |
+
"""
|
| 36 |
try:
|
| 37 |
if not raw_data or not raw_data.get('ohlcv'):
|
|
|
|
| 38 |
return None
|
| 39 |
|
| 40 |
symbol = raw_data['symbol']
|
|
|
|
| 41 |
|
| 42 |
base_analysis = await self.process_and_score_symbol(raw_data)
|
| 43 |
if not base_analysis:
|
| 44 |
return None
|
| 45 |
|
| 46 |
try:
|
| 47 |
+
# حساب المؤشرات (لا تغيير)
|
| 48 |
advanced_indicators = {}
|
| 49 |
+
ohlcv_available = raw_data.get('ohlcv', {})
|
| 50 |
+
for timeframe, candles in ohlcv_available.items():
|
| 51 |
+
if candles and len(candles) >= 20:
|
| 52 |
dataframe = self._create_dataframe(candles)
|
|
|
|
| 53 |
indicators = self.technical_analyzer.calculate_all_indicators(dataframe, timeframe)
|
| 54 |
advanced_indicators[timeframe] = indicators
|
|
|
|
| 55 |
base_analysis['advanced_indicators'] = advanced_indicators
|
| 56 |
|
| 57 |
+
# حساب مونت كارلو (لا تغيير)
|
| 58 |
+
monte_carlo_probability = await self.monte_carlo_analyzer.predict_1h_probability(ohlcv_available)
|
| 59 |
if monte_carlo_probability is not None:
|
| 60 |
base_analysis['monte_carlo_probability'] = monte_carlo_probability
|
| 61 |
base_analysis['monte_carlo_details'] = self.monte_carlo_analyzer.simulation_results
|
| 62 |
|
| 63 |
+
# حساب الأنماط (لا تغيير)
|
| 64 |
+
pattern_analysis = await self.pattern_analyzer.detect_chart_patterns(ohlcv_available)
|
| 65 |
base_analysis['pattern_analysis'] = pattern_analysis
|
| 66 |
|
| 67 |
+
# 🔴 --- تعديل: استخدام بيانات الحيتان المحملة مسبقاً --- 🔴
|
| 68 |
+
# لا يوجد استدعاء شبكي هنا
|
| 69 |
+
if preloaded_whale_data:
|
| 70 |
+
base_analysis['whale_data'] = preloaded_whale_data.get(symbol, {'data_available': False, 'reason': 'Not preloaded'})
|
| 71 |
+
else:
|
| 72 |
+
base_analysis['whale_data'] = {'data_available': False, 'reason': 'Preloading disabled'}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
# 🔴 --- نهاية التعديل --- 🔴
|
| 74 |
|
| 75 |
+
# حساب الاستراتيجيات والنتيجة النهائية (لا تغيير في المنطق هنا)
|
| 76 |
strategy_scores, base_scores = await self.strategy_engine.evaluate_all_strategies(base_analysis, self.market_context)
|
| 77 |
base_analysis['strategy_scores'] = strategy_scores
|
| 78 |
base_analysis['base_strategy_scores'] = base_scores
|
| 79 |
|
|
|
|
| 80 |
if base_scores:
|
| 81 |
+
# ... (الكود الخاص بتحديد أفضل استراتيجية كما هو) ...
|
| 82 |
best_strategy = max(base_scores.items(), key=lambda x: x[1])
|
| 83 |
best_strategy_name = best_strategy[0]
|
| 84 |
best_strategy_score = best_strategy[1]
|
| 85 |
base_analysis['recommended_strategy'] = best_strategy_name
|
| 86 |
base_analysis['strategy_confidence'] = best_strategy_score
|
| 87 |
+
base_analysis['target_strategy'] = best_strategy_name if best_strategy_score > 0.3 else 'GENERIC'
|
| 88 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
enhanced_score = self._calculate_enhanced_final_score(base_analysis)
|
| 91 |
base_analysis['enhanced_final_score'] = enhanced_score
|
| 92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
return base_analysis
|
| 94 |
|
| 95 |
except Exception as strategy_error:
|
| 96 |
print(f"❌ خطأ في التحليل المتقدم لـ {symbol}: {strategy_error}")
|
|
|
|
| 97 |
return base_analysis
|
| 98 |
|
| 99 |
except Exception as error:
|
| 100 |
print(f"❌ خطأ فادح في المعالجة المحسنة للرمز {raw_data.get('symbol', 'unknown')}: {error}")
|
| 101 |
+
return None
|
| 102 |
+
|
| 103 |
+
# ...(بقية الدوال في الملف كما هي بدون تغيير، بما في ذلك _calculate_enhanced_final_score)...
|
| 104 |
|
|
|
|
|
|
|
| 105 |
def _create_dataframe(self, candles):
|
| 106 |
"""إنشاء DataFrame من بيانات الشموع مع DatetimeIndex مرتب"""
|
| 107 |
try:
|
| 108 |
+
if not candles: return pd.DataFrame()
|
|
|
|
|
|
|
| 109 |
df = pd.DataFrame(candles, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
| 110 |
df[['open', 'high', 'low', 'close', 'volume']] = df[['open', 'high', 'low', 'close', 'volume']].astype(float)
|
|
|
|
|
|
|
| 111 |
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
|
| 112 |
df.set_index('timestamp', inplace=True)
|
|
|
|
|
|
|
| 113 |
df.sort_index(inplace=True)
|
|
|
|
| 114 |
return df
|
| 115 |
except Exception as e:
|
| 116 |
print(f"❌ خطأ في إنشاء DataFrame: {e}")
|
| 117 |
return pd.DataFrame()
|
| 118 |
+
|
| 119 |
def _calculate_enhanced_final_score(self, analysis):
|
| 120 |
"""حساب الدرجة النهائية المحسنة"""
|
| 121 |
try:
|
|
|
|
| 123 |
monte_carlo_score = analysis.get('monte_carlo_probability', 0)
|
| 124 |
pattern_confidence = analysis.get('pattern_analysis', {}).get('pattern_confidence', 0)
|
| 125 |
strategy_confidence = analysis.get('strategy_confidence', 0)
|
|
|
|
| 126 |
whale_confidence = 0
|
| 127 |
whale_data = analysis.get('whale_data')
|
| 128 |
if whale_data and whale_data.get('data_available'):
|
| 129 |
signal = whale_data.get('trading_signal', {})
|
|
|
|
| 130 |
if signal.get('action') != 'HOLD' and signal.get('confidence', 0) >= 0.5:
|
| 131 |
whale_confidence = signal.get('confidence', 0)
|
| 132 |
|
|
|
|
| 133 |
components = []
|
| 134 |
weights = []
|
| 135 |
|
| 136 |
+
if base_score > 0: components.append(base_score); weights.append(0.20)
|
| 137 |
+
if monte_carlo_score > 0: components.append(monte_carlo_score); weights.append(0.25)
|
| 138 |
+
if pattern_confidence > 0: components.append(pattern_confidence); weights.append(0.25)
|
| 139 |
+
if strategy_confidence > 0: components.append(strategy_confidence); weights.append(0.15)
|
| 140 |
+
if whale_confidence > 0: components.append(whale_confidence); weights.append(0.15)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
+
if not components: return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
|
|
|
| 144 |
total_weight = sum(weights)
|
| 145 |
+
if total_weight == 0: return 0
|
|
|
|
| 146 |
|
| 147 |
enhanced_score = sum(comp * weight for comp, weight in zip(components, weights)) / total_weight
|
| 148 |
|
| 149 |
+
return min(max(enhanced_score, 0.0), 1.0)
|
| 150 |
|
| 151 |
except Exception as e:
|
| 152 |
print(f"❌ خطأ في حساب الدرجة المحسنة: {e}")
|
| 153 |
+
return analysis.get('final_score', 0)
|
| 154 |
+
|
| 155 |
async def process_and_score_symbol(self, raw_data):
|
| 156 |
"""المعالجة الأساسية للرمز"""
|
| 157 |
try:
|
| 158 |
symbol = raw_data['symbol']
|
| 159 |
+
ohlcv_data = raw_data.get('ohlcv')
|
| 160 |
|
| 161 |
+
if not ohlcv_data: return None
|
|
|
|
|
|
|
|
|
|
| 162 |
|
| 163 |
current_price = raw_data.get('current_price', 0)
|
| 164 |
layer1_score = raw_data.get('layer1_score', 0)
|
| 165 |
reasons = raw_data.get('reasons_for_candidacy', [])
|
|
|
|
| 166 |
final_score = layer1_score
|
| 167 |
|
| 168 |
return {
|
| 169 |
+
'symbol': symbol, 'current_price': current_price, 'final_score': final_score,
|
| 170 |
+
'enhanced_final_score': final_score, 'reasons_for_candidacy': reasons,
|
| 171 |
+
'layer1_score': layer1_score, 'ohlcv': ohlcv_data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
}
|
|
|
|
| 173 |
except Exception as error:
|
| 174 |
print(f"❌ خطأ في المعالجة الأساسية للرمز {raw_data.get('symbol', 'unknown')}: {error}")
|
| 175 |
return None
|
| 176 |
+
|
| 177 |
def filter_top_candidates(self, candidates, number_of_candidates=10):
|
| 178 |
"""تصفية أفضل المرشحين"""
|
| 179 |
+
valid_candidates = [c for c in candidates if c is not None and isinstance(c, dict)]
|
| 180 |
+
if not valid_candidates: print("❌ لا توجد مرشحات صالحة للتصفية"); return []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
|
| 182 |
+
sorted_candidates = sorted(valid_candidates, key=lambda c: c.get('enhanced_final_score', 0), reverse=True)
|
| 183 |
top_candidates = sorted_candidates[:number_of_candidates]
|
| 184 |
|
| 185 |
print(f"🎖️ أفضل {len(top_candidates)} مرشح:")
|
| 186 |
+
for i, c in enumerate(top_candidates):
|
| 187 |
+
score = c.get('enhanced_final_score', 0)
|
| 188 |
+
strategy = c.get('recommended_strategy', 'GENERIC')
|
| 189 |
+
mc_score = c.get('monte_carlo_probability')
|
| 190 |
+
pattern = c.get('pattern_analysis', {}).get('pattern_detected', 'no_pattern')
|
| 191 |
+
symbol = c.get('symbol', 'UNKNOWN')
|
| 192 |
+
|
| 193 |
+
print(f" {i+1}. {symbol}: 📊 {score:.3f}")
|
| 194 |
+
if mc_score is not None: print(f" 🎯 مونت كارلو: {mc_score:.3f}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
print(f" 🎯 استراتيجية: {strategy} | نمط: {pattern}")
|
| 196 |
+
whale_data = c.get('whale_data')
|
|
|
|
| 197 |
if whale_data and whale_data.get('data_available'):
|
| 198 |
signal = whale_data.get('trading_signal', {})
|
| 199 |
+
print(f" 🐋 حيتان: {signal.get('action', 'HOLD')} (ثقة: {signal.get('confidence', 0):.2f})")
|
|
|
|
|
|
|
|
|
|
| 200 |
return top_candidates
|
| 201 |
|
| 202 |
+
# (safe_json_parse و process_multiple_symbols_parallel كما هي، لكن process_multiple_symbols_parallel لم تعد تُستخدم مباشرة في التدفق الجديد)
|
| 203 |
def safe_json_parse(json_string):
|
| 204 |
+
if not json_string: return None
|
|
|
|
|
|
|
|
|
|
| 205 |
try:
|
| 206 |
return json.loads(json_string)
|
| 207 |
except json.JSONDecodeError as e:
|
| 208 |
try:
|
| 209 |
+
s = str(json_string).replace("'", '"')
|
| 210 |
+
s = re.sub(r'\\"', '"', s)
|
| 211 |
+
s = re.sub(r'[\n\t]', ' ', s)
|
| 212 |
+
s = re.sub(r'(?<!")(\b\w+\b)(?=\s*:)', r'"\1"', s)
|
| 213 |
+
s = re.sub(r':\s*(\btrue\b|\bfalse\b|\bnull\b)(?=[,\s}])', r': \1', s)
|
| 214 |
+
s = re.sub(r',\s*([}\]])', r'\1', s)
|
| 215 |
+
return json.loads(s)
|
| 216 |
+
except json.JSONDecodeError: return None
|
| 217 |
+
|
| 218 |
+
async def process_multiple_symbols_parallel(self, symbols_data_list, preloaded_whale_data: dict, max_concurrent=5):
|
| 219 |
+
"""(معدلة) معالجة متعددة للرموز بشكل متوازٍ باستخدام بيانات الحيتان المحملة مسبقًا"""
|
| 220 |
+
# 🔴 استخدام Semaphore داخلي للتحكم في مهام ML نفسها (وليس الحيتان)
|
| 221 |
+
semaphore = asyncio.Semaphore(max_concurrent)
|
| 222 |
+
tasks_results = []
|
|
|
|
|
|
|
|
|
|
| 223 |
|
| 224 |
+
async def process_symbol_with_semaphore(symbol_data):
|
| 225 |
+
async with semaphore:
|
| 226 |
+
try:
|
| 227 |
+
# تمرير بيانات الحيتان المحملة مسبقًا
|
| 228 |
+
return await self.process_and_score_symbol_enhanced(symbol_data, preloaded_whale_data)
|
| 229 |
+
except Exception as e:
|
| 230 |
+
return e
|
| 231 |
|
|
|
|
|
|
|
| 232 |
try:
|
| 233 |
+
# print(f"🚀 بدء المعالجة المتوازية لـ {len(symbols_data_list)} رمز (ML بحد أقصى {max_concurrent})...") # Reduced logging
|
| 234 |
|
| 235 |
+
batch_tasks = [asyncio.create_task(process_symbol_with_semaphore(sd)) for sd in symbols_data_list]
|
| 236 |
+
batch_results = await asyncio.gather(*batch_tasks, return_exceptions=False)
|
| 237 |
+
|
| 238 |
+
successful_results = []
|
| 239 |
+
for result in batch_results:
|
| 240 |
+
if isinstance(result, Exception):
|
| 241 |
+
# print(f" ⚠️ خطأ في مهمة ML متوازية: {result}") # Reduced logging
|
| 242 |
+
continue
|
| 243 |
+
if isinstance(result, dict) and result.get('enhanced_final_score', 0) > 0.4:
|
| 244 |
+
successful_results.append(result)
|
| 245 |
|
| 246 |
+
# print(f"🎯 اكتملت المعالجة المتوازية ل��مجموعة: {len(successful_results)}/{len(symbols_data_list)} ناجحة") # Reduced logging
|
| 247 |
+
return successful_results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
|
| 249 |
except Exception as error:
|
| 250 |
+
print(f"❌ خطأ في المعالجة المتوازية لـ ML: {error}")
|
| 251 |
return []
|
| 252 |
|
| 253 |
+
|
| 254 |
+
print("✅ ML Processor loaded - Non-Blocking Whale Data Integration")
|