Update ml_engine/ranker.py
Browse files- ml_engine/ranker.py +35 -83
ml_engine/ranker.py
CHANGED
|
@@ -1,110 +1,62 @@
|
|
| 1 |
-
# ml_engine/ranker.py (
|
| 2 |
import pandas as pd
|
| 3 |
import numpy as np
|
| 4 |
import os
|
|
|
|
| 5 |
from typing import List, Dict, Any
|
| 6 |
|
| 7 |
try:
|
| 8 |
import lightgbm as lgb
|
| 9 |
LGBM_AVAILABLE = True
|
| 10 |
except ImportError:
|
| 11 |
-
print("❌❌ [Ranker
|
| 12 |
-
print(" -> قم بتثبيتها: pip install lightgbm")
|
| 13 |
LGBM_AVAILABLE = False
|
| 14 |
|
| 15 |
class Layer1Ranker:
|
| 16 |
-
def __init__(self, model_path: str = "ml_models/layer1_ranker.lgbm"
|
| 17 |
-
""
|
| 18 |
-
تهيئة "العقل الذكي" (نموذج V9.1) للكاشف المصغر.
|
| 19 |
-
|
| 20 |
-
Args:
|
| 21 |
-
model_path (str): المسار المحلي إلى ملف نموذج LightGBM المدرب.
|
| 22 |
-
r2_service: (اختياري) خدمة R2 لتحميل النموذج من السحابة.
|
| 23 |
-
"""
|
| 24 |
-
if not LGBM_AVAILABLE:
|
| 25 |
-
raise ImportError("مكتبة 'lightgbm' مطلوبة لتشغيل الرانكر.")
|
| 26 |
-
|
| 27 |
self.model_path = model_path
|
| 28 |
self.model_name = os.path.basename(model_path)
|
| 29 |
-
self.r2_service = r2_service # (للاستخدام المستقبلي لتحميل النموذج من R2)
|
| 30 |
self.model = None
|
| 31 |
-
self.features_in_ = None
|
| 32 |
|
| 33 |
async def initialize(self):
|
| 34 |
-
"""
|
| 35 |
-
|
| 36 |
-
(ملاحظة: حالياً يحمل من ملف محلي. يمكن تعديله ليحمل من R2)
|
| 37 |
-
"""
|
| 38 |
-
print(f"🔄 [Ranker V9.1] بدء تحميل نموذج الكاشف المصغر: {self.model_name}...")
|
| 39 |
try:
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
# (الخطة ب: التحميل من ملف محلي إذا فشلت الخطة أ أو لم تكن مفعلة)
|
| 49 |
-
if self.model is None:
|
| 50 |
-
if not os.path.exists(self.model_path):
|
| 51 |
-
print(f"❌❌ [Ranker V9.1] خطأ فادح: ملف النموذج {self.model_path} غير موجود.")
|
| 52 |
-
print(" -> هل قمت بتنزيل النموذج 'layer1_ranker.lgbm' من Colab؟")
|
| 53 |
-
print(" -> سيتم استخدام 'نموذج وهمي' (Placeholder) مؤقت. هذا سيمنع أي تداول.")
|
| 54 |
-
self.model = None
|
| 55 |
-
# (هذه هي الميزات التي نتوقعها من 'indicators.py' V9.1)
|
| 56 |
-
self.features_in_ = [
|
| 57 |
-
'price_to_ema_50', 'price_to_ema_200', 'price_to_min_100',
|
| 58 |
-
'price_to_max_100', 'slope_14_50', 'volume_zscore_50',
|
| 59 |
-
'vwap_gap', 'rsi_14', 'rsi_mean_10', 'rsi_std_10',
|
| 60 |
-
'mfi_14', 'mfi_mean_10', 'adx_14', 'atr_percent',
|
| 61 |
-
'atr_normalized_return'
|
| 62 |
-
]
|
| 63 |
-
else:
|
| 64 |
-
# (التحميل الناجح من الملف المحلي)
|
| 65 |
-
self.model = lgb.Booster(model_file=self.model_path)
|
| 66 |
-
self.features_in_ = self.model.feature_name()
|
| 67 |
-
print(f"✅ [Ranker V9.1] تم تحميل النموذج {self.model_name} بنجاح من ملف محلي.")
|
| 68 |
-
print(f" -> عدد الميزات المطلوبة: {len(self.features_in_)}")
|
| 69 |
-
|
| 70 |
except Exception as e:
|
| 71 |
-
print(f"❌ [Ranker
|
| 72 |
self.model = None
|
| 73 |
|
| 74 |
def predict_proba(self, features_df: pd.DataFrame) -> np.ndarray:
|
| 75 |
-
"""
|
| 76 |
-
|
| 77 |
-
"""
|
| 78 |
-
if self.model is None:
|
| 79 |
-
# (الوضع الآمن: إذا لم يتم تحميل النموذج، إرجاع أصفار)
|
| 80 |
-
print("⚠️ [Ranker V9.1] التنبؤ باستخدام 'الوضع الوهمي' (Placeholder). إرجاع 0.0")
|
| 81 |
-
return np.zeros(len(features_df))
|
| 82 |
-
|
| 83 |
try:
|
| 84 |
-
#
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
print(f" -> ميزات مفقودة: {missing_features}")
|
| 90 |
-
return np.zeros(len(features_df))
|
| 91 |
-
|
| 92 |
-
aligned_df = features_df[self.features_in_]
|
| 93 |
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
# (التحقق مما إذا كان النموذج يعيد فئة واحدة أو اثنتين)
|
| 98 |
-
if probabilities.ndim == 2:
|
| 99 |
-
# (الحالة القياسية: [prob_0, prob_1])
|
| 100 |
-
return probabilities[:, 1]
|
| 101 |
-
else:
|
| 102 |
-
# (الحالة الأخرى: يعيد prob_1 فقط)
|
| 103 |
-
return probabilities
|
| 104 |
-
|
| 105 |
except Exception as e:
|
| 106 |
-
print(f"❌ [Ranker
|
| 107 |
-
# (إرجاع أصفار في حالة الفشل)
|
| 108 |
return np.zeros(len(features_df))
|
| 109 |
|
| 110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ml_engine/ranker.py (V11.1 - Layer 1 Smart Ranker with Memory Management)
|
| 2 |
import pandas as pd
|
| 3 |
import numpy as np
|
| 4 |
import os
|
| 5 |
+
import gc # استيراد لجمع القمامة
|
| 6 |
from typing import List, Dict, Any
|
| 7 |
|
| 8 |
try:
|
| 9 |
import lightgbm as lgb
|
| 10 |
LGBM_AVAILABLE = True
|
| 11 |
except ImportError:
|
| 12 |
+
print("❌❌ [Ranker] مكتبة 'lightgbm' غير موجودة.")
|
|
|
|
| 13 |
LGBM_AVAILABLE = False
|
| 14 |
|
| 15 |
class Layer1Ranker:
|
| 16 |
+
def __init__(self, model_path: str = "ml_models/layer1_ranker.lgbm"):
|
| 17 |
+
if not LGBM_AVAILABLE: raise ImportError("lightgbm required")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
self.model_path = model_path
|
| 19 |
self.model_name = os.path.basename(model_path)
|
|
|
|
| 20 |
self.model = None
|
| 21 |
+
self.features_in_ = None
|
| 22 |
|
| 23 |
async def initialize(self):
|
| 24 |
+
"""تحميل النموذج من الملف المحلي"""
|
| 25 |
+
print(f"🔄 [Ranker] تحميل {self.model_name}...")
|
|
|
|
|
|
|
|
|
|
| 26 |
try:
|
| 27 |
+
if os.path.exists(self.model_path):
|
| 28 |
+
self.model = lgb.Booster(model_file=self.model_path)
|
| 29 |
+
self.features_in_ = self.model.feature_name()
|
| 30 |
+
print(f"✅ [Ranker] تم تحميل النموذج بنجاح ({len(self.features_in_)} features).")
|
| 31 |
+
else:
|
| 32 |
+
print(f"❌ [Ranker] ملف النموذج غير موجود: {self.model_path}")
|
| 33 |
+
self.model = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
except Exception as e:
|
| 35 |
+
print(f"❌ [Ranker] فشل تحميل النموذج: {e}")
|
| 36 |
self.model = None
|
| 37 |
|
| 38 |
def predict_proba(self, features_df: pd.DataFrame) -> np.ndarray:
|
| 39 |
+
"""التنبؤ باحتمالية الصعود"""
|
| 40 |
+
if self.model is None: return np.zeros(len(features_df))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
try:
|
| 42 |
+
# التأكد من ترتيب الأعمدة
|
| 43 |
+
if not all(f in features_df.columns for f in self.features_in_):
|
| 44 |
+
# محاولة تعبئة الميزات المفقودة بأصفار إذا لزم الأمر
|
| 45 |
+
for f in self.features_in_:
|
| 46 |
+
if f not in features_df.columns: features_df[f] = 0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
+
aligned_df = features_df[self.features_in_]
|
| 49 |
+
return self.model.predict(aligned_df)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
except Exception as e:
|
| 51 |
+
print(f"❌ [Ranker] خطأ التنبؤ: {e}")
|
|
|
|
| 52 |
return np.zeros(len(features_df))
|
| 53 |
|
| 54 |
+
# 🔴 دالة جديدة لتنظيف الذاكرة
|
| 55 |
+
def clear_memory(self):
|
| 56 |
+
"""تحرير نموذج LightGBM من الذاكرة"""
|
| 57 |
+
self.model = None
|
| 58 |
+
self.features_in_ = None
|
| 59 |
+
gc.collect()
|
| 60 |
+
print(f"🧹 [Ranker] تم تحرير النموذج من الذاكرة.")
|
| 61 |
+
|
| 62 |
+
print("✅ ML Module: Layer 1 Ranker V11.1 (Memory Managed) loaded.")
|