Spaces:
Running
Running
| # ml_engine/ranker.py (V9.1 - Layer 1 Smart Ranker) | |
| import pandas as pd | |
| import numpy as np | |
| import os | |
| from typing import List, Dict, Any | |
| try: | |
| import lightgbm as lgb | |
| LGBM_AVAILABLE = True | |
| except ImportError: | |
| print("❌❌ [Ranker V9.1] مكتبة 'lightgbm' غير موجودة. الرانكر سيفشل.") | |
| print(" -> قم بتثبيتها: pip install lightgbm") | |
| LGBM_AVAILABLE = False | |
| class Layer1Ranker: | |
| def __init__(self, model_path: str = "ml_models/layer1_ranker.lgbm", r2_service=None): | |
| """ | |
| تهيئة "العقل الذكي" (نموذج V9.1) للكاشف المصغر. | |
| Args: | |
| model_path (str): المسار المحلي إلى ملف نموذج LightGBM المدرب. | |
| r2_service: (اختياري) خدمة R2 لتحميل النموذج من السحابة. | |
| """ | |
| if not LGBM_AVAILABLE: | |
| raise ImportError("مكتبة 'lightgbm' مطلوبة لتشغيل الرانكر.") | |
| self.model_path = model_path | |
| self.model_name = os.path.basename(model_path) | |
| self.r2_service = r2_service # (للاستخدام المستقبلي لتحميل النموذج من R2) | |
| self.model = None | |
| self.features_in_ = None # (قائمة الميزات التي تدرب عليها النموذج) | |
| async def initialize(self): | |
| """ | |
| تحميل النموذج المدرب وميزاته. | |
| (ملاحظة: حالياً يحمل من ملف محلي. يمكن تعديله ليحمل من R2) | |
| """ | |
| print(f"🔄 [Ranker V9.1] بدء تحميل نموذج الكاشف المصغر: {self.model_name}...") | |
| try: | |
| # (الخطة أ: التحميل من R2 إذا كان متاحاً - غير مفعل حالياً) | |
| # if self.r2_service: | |
| # print(f" -> (محاولة التحميل من R2...)") | |
| # model_bytes = await self.r2_service.load_model_from_r2(self.model_name) | |
| # if model_bytes: | |
| # self.model = lgb.Booster(model_str=model_bytes.decode('utf-8')) | |
| # print(" -> (تم التحميل بنجاح من R2)") | |
| # (الخطة ب: التحميل من ملف محلي إذا فشلت الخطة أ أو لم تكن مفعلة) | |
| if self.model is None: | |
| if not os.path.exists(self.model_path): | |
| print(f"❌❌ [Ranker V9.1] خطأ فادح: ملف النموذج {self.model_path} غير موجود.") | |
| print(" -> هل قمت بتنزيل النموذج 'layer1_ranker.lgbm' من Colab؟") | |
| print(" -> سيتم استخدام 'نموذج وهمي' (Placeholder) مؤقت. هذا سيمنع أي تداول.") | |
| self.model = None | |
| # (هذه هي الميزات التي نتوقعها من 'indicators.py' V9.1) | |
| self.features_in_ = [ | |
| 'price_to_ema_50', 'price_to_ema_200', 'price_to_min_100', | |
| 'price_to_max_100', 'slope_14_50', 'volume_zscore_50', | |
| 'vwap_gap', 'rsi_14', 'rsi_mean_10', 'rsi_std_10', | |
| 'mfi_14', 'mfi_mean_10', 'adx_14', 'atr_percent', | |
| 'atr_normalized_return' | |
| ] | |
| else: | |
| # (التحميل الناجح من الملف المحلي) | |
| self.model = lgb.Booster(model_file=self.model_path) | |
| self.features_in_ = self.model.feature_name() | |
| print(f"✅ [Ranker V9.1] تم تحميل النموذج {self.model_name} بنجاح من ملف محلي.") | |
| print(f" -> عدد الميزات المطلوبة: {len(self.features_in_)}") | |
| except Exception as e: | |
| print(f"❌ [Ranker V9.1] فشل فادح في تحميل النموذج {self.model_name}: {e}") | |
| self.model = None | |
| def predict_proba(self, features_df: pd.DataFrame) -> np.ndarray: | |
| """ | |
| التنبؤ باحتمالية "الانطلاقة" (الدرجة 0.0 إلى 1.0) لدفعة من العملات. | |
| """ | |
| if self.model is None: | |
| # (الوضع الآمن: إذا لم يتم تحميل النموذج، إرجاع أصفار) | |
| print("⚠️ [Ranker V9.1] التنبؤ باستخدام 'الوضع الوهمي' (Placeholder). إرجاع 0.0") | |
| return np.zeros(len(features_df)) | |
| try: | |
| # (التأكد من أن DataFrame يحتوي على الميزات المطلوبة بنفس الترتيب) | |
| # (هذا يحمي من أخطاء ترتيب الأعمدة) | |
| if not all(feature in features_df.columns for feature in self.features_in_): | |
| print("❌ [Ranker V9.1] خطأ: الميزات المدخلة لا تتطابق مع ميزات النموذج.") | |
| missing_features = set(self.features_in_) - set(features_df.columns) | |
| print(f" -> ميزات مفقودة: {missing_features}") | |
| return np.zeros(len(features_df)) | |
| aligned_df = features_df[self.features_in_] | |
| # (التنبؤ بالاحتمالية - [:, 1] هي احتمالية الفئة "1" (انطلاقة)) | |
| probabilities = self.model.predict(aligned_df) | |
| # (التحقق مما إذا كان النموذج يعيد فئة واحدة أو اثنتين) | |
| if probabilities.ndim == 2: | |
| # (الحالة القياسية: [prob_0, prob_1]) | |
| return probabilities[:, 1] | |
| else: | |
| # (الحالة الأخرى: يعيد prob_1 فقط) | |
| return probabilities | |
| except Exception as e: | |
| print(f"❌ [Ranker V9.1] خطأ أثناء التنبؤ: {e}") | |
| # (إرجاع أصفار في حالة الفشل) | |
| return np.zeros(len(features_df)) | |
| print("✅ ML Module: Layer 1 Ranker (V9.1) loaded.") |