Trad / ml_engine /ranker.py
Riy777's picture
Create ranker.py
5acd124
raw
history blame
6.12 kB
# 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.")