Spaces:
Paused
Paused
Update ml_engine/oracle_engine.py
Browse files- ml_engine/oracle_engine.py +36 -44
ml_engine/oracle_engine.py
CHANGED
|
@@ -10,19 +10,13 @@ from typing import Dict, Any, List, Optional
|
|
| 10 |
# --- [ 0. إعدادات ] ---
|
| 11 |
warnings.filterwarnings('ignore', category=FutureWarning)
|
| 12 |
|
| 13 |
-
# (هذه هي نفس الإعدادات المستخدمة في التدريب)
|
| 14 |
PIPELINE_SETTINGS = {
|
| 15 |
'SWING_PROMINENCE_PCT': 0.02, # 2%
|
| 16 |
}
|
| 17 |
|
| 18 |
-
# (هذه هي العتبة التي سنثق بها في قرارات اللجنة)
|
| 19 |
-
# [cite_start]بناءً على تقريرك، الدقة فوق 75% ممتازة [cite: 5-8]
|
| 20 |
DECISION_CONFIDENCE_THRESHOLD = 0.75
|
| 21 |
-
|
| 22 |
-
# (عدد نماذج اللجنة)
|
| 23 |
N_STRATEGY_MODELS = 11
|
| 24 |
|
| 25 |
-
# (أسماء الفئات)
|
| 26 |
STRATEGY_MAP = {
|
| 27 |
0: 'WAIT',
|
| 28 |
1: 'SWING_LONG',
|
|
@@ -39,10 +33,6 @@ class OracleEngine:
|
|
| 39 |
def __init__(self, model_dir: str = "ml_models/Unified_Models_V1"):
|
| 40 |
"""
|
| 41 |
تهيئة "العقل" الاحتمالي (L3 Oracle).
|
| 42 |
-
|
| 43 |
-
Args:
|
| 44 |
-
model_dir (str): المسار إلى المجلد الذي يحتوي على
|
| 45 |
-
الـ 15 نموذجاً المدربة.
|
| 46 |
"""
|
| 47 |
self.model_dir = model_dir
|
| 48 |
self.strategy_boosters: List[lgb.Booster] = []
|
|
@@ -50,7 +40,8 @@ class OracleEngine:
|
|
| 50 |
|
| 51 |
self.feature_names: List[str] = []
|
| 52 |
self.initialized = False
|
| 53 |
-
|
|
|
|
| 54 |
|
| 55 |
async def initialize(self):
|
| 56 |
"""
|
|
@@ -59,7 +50,7 @@ class OracleEngine:
|
|
| 59 |
if self.initialized:
|
| 60 |
return True
|
| 61 |
|
| 62 |
-
print(f"🧠 [OracleEngine V2.
|
| 63 |
try:
|
| 64 |
# 1. تحميل نماذج "لجنة القرار" (Strategy Ensemble)
|
| 65 |
for i in range(N_STRATEGY_MODELS):
|
|
@@ -88,12 +79,12 @@ class OracleEngine:
|
|
| 88 |
self.feature_names = self.strategy_boosters[0].feature_name()
|
| 89 |
self.initialized = True
|
| 90 |
|
| 91 |
-
print(f"✅ [OracleEngine V2.
|
| 92 |
print(f" -> سيعمل على الأطر: {TIMEBYTES_TO_PROCESS}")
|
| 93 |
return True
|
| 94 |
|
| 95 |
except Exception as e:
|
| 96 |
-
print(f"❌ [OracleEngine V2.
|
| 97 |
self.initialized = False
|
| 98 |
return False
|
| 99 |
|
|
@@ -137,7 +128,6 @@ class OracleEngine:
|
|
| 137 |
df['volume_zscore'] = (df['volume'] - df['volume'].rolling(50).mean()) / (df['volume'].rolling(50).std() + 1e-9)
|
| 138 |
df['dist_from_EMA200_pct'] = (df['close'] - df['EMA_200']) / (df['EMA_200'] + 1e-9)
|
| 139 |
|
| 140 |
-
# (إصلاح Bollinger Bands كما في كود التجميع)
|
| 141 |
bbu_col = next((col for col in df.columns if 'BBU_20_2.0' in str(col)), None)
|
| 142 |
bbl_col = next((col for col in df.columns if 'BBL_20_2.0' in str(col)), None)
|
| 143 |
bbm_col = next((col for col in df.columns if 'BBM_20_2.0' in str(col)), None)
|
|
@@ -171,18 +161,23 @@ class OracleEngine:
|
|
| 171 |
df = self._calculate_fibonacci_matrix(df)
|
| 172 |
df = self._calculate_alpha_strategies(df)
|
| 173 |
|
| 174 |
-
# ملء أي قيم NaN
|
| 175 |
df = df.ffill().bfill()
|
| 176 |
|
| 177 |
# أخذ آخر صف فقط
|
| 178 |
latest_features = df.iloc[-1:]
|
| 179 |
|
| 180 |
-
# التأكد من وجود جميع الميزات بالترتيب الصحيح
|
| 181 |
try:
|
| 182 |
feature_vector = latest_features[self.feature_names]
|
| 183 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
if feature_vector.isnull().values.any():
|
| 185 |
-
print("⚠️ [Oracle Warning] Feature vector contains NaN after fill.")
|
| 186 |
return None
|
| 187 |
return feature_vector
|
| 188 |
except Exception as e:
|
|
@@ -193,7 +188,6 @@ class OracleEngine:
|
|
| 193 |
async def predict(self, symbol_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 194 |
"""
|
| 195 |
الدالة الرئيسية: تحليل إشارة مرشحة وإرجاع قرار كامل.
|
| 196 |
-
(هذه الدالة تحل محل llm_service.get_trading_decision)
|
| 197 |
"""
|
| 198 |
if not self.initialized:
|
| 199 |
return {'action': 'WAIT', 'reason': 'Oracle Engine not initialized'}
|
|
@@ -211,26 +205,33 @@ class OracleEngine:
|
|
| 211 |
feature_vector = self._create_feature_vector(ohlcv_data.get(tf))
|
| 212 |
|
| 213 |
if feature_vector is None:
|
| 214 |
-
print(f" -> {symbol_data['symbol']} @ {tf}: Skipped (Insufficient data)")
|
| 215 |
continue
|
| 216 |
|
| 217 |
-
# 1. تشغيل "لجنة القرار" (Strategy Ensemble)
|
| 218 |
all_probs = [
|
| 219 |
booster.predict(feature_vector, num_iteration=booster.best_iteration)
|
| 220 |
for booster in self.strategy_boosters
|
| 221 |
]
|
| 222 |
-
ensemble_probs = np.mean(all_probs, axis=0)[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
|
| 224 |
-
#
|
| 225 |
-
predicted_strategy_idx = np.argmax(
|
| 226 |
-
confidence =
|
| 227 |
strategy_name = STRATEGY_MAP.get(predicted_strategy_idx, 'WAIT')
|
|
|
|
| 228 |
|
| 229 |
all_tf_decisions.append({
|
| 230 |
'timeframe': tf,
|
| 231 |
'strategy': strategy_name,
|
| 232 |
'confidence': float(confidence),
|
| 233 |
-
'feature_vector': feature_vector
|
| 234 |
})
|
| 235 |
|
| 236 |
if not all_tf_decisions:
|
|
@@ -243,25 +244,16 @@ class OracleEngine:
|
|
| 243 |
confidence = best_decision['confidence']
|
| 244 |
best_tf = best_decision['timeframe']
|
| 245 |
|
| 246 |
-
# --- [
|
| 247 |
-
# (السبب 1: الثقة ضعيفة)
|
| 248 |
if confidence < DECISION_CONFIDENCE_THRESHOLD:
|
| 249 |
return {
|
| 250 |
'action': 'IGNORE',
|
| 251 |
-
'reason': f"
|
| 252 |
'confidence': confidence,
|
| 253 |
'strategy': strategy_name
|
| 254 |
}
|
| 255 |
|
| 256 |
-
# (
|
| 257 |
-
if strategy_name == 'WAIT':
|
| 258 |
-
return {
|
| 259 |
-
'action': 'IGNORE',
|
| 260 |
-
'reason': f"Oracle Consensus is 'WAIT' (Conf: {confidence:.2f}) @ {best_tf}",
|
| 261 |
-
'confidence': confidence,
|
| 262 |
-
'strategy': strategy_name
|
| 263 |
-
}
|
| 264 |
-
# --- [ نهاية التعديل ] ---
|
| 265 |
|
| 266 |
# --- [ الخطوة 4: (نجحت الثقة) - تشغيل "لجنة الأهداف" ] ---
|
| 267 |
winning_feature_vector = best_decision['feature_vector']
|
|
@@ -283,18 +275,18 @@ class OracleEngine:
|
|
| 283 |
elif "SHORT" in strategy_name:
|
| 284 |
tp_price = current_price * (1 - tp_pct)
|
| 285 |
sl_price = current_price * (1 + sl_pct)
|
| 286 |
-
action_type = "SELL"
|
| 287 |
else:
|
| 288 |
-
# (هذا
|
| 289 |
return {'action': 'IGNORE', 'reason': 'Strategy not actionable'}
|
| 290 |
|
| 291 |
# --- [ الخطوة 6: إرجاع القرار الكامل ] ---
|
| 292 |
return {
|
| 293 |
-
'action': 'WATCH',
|
| 294 |
'confidence': confidence,
|
| 295 |
'analysis_summary': f"Oracle Consensus @ {best_tf}: {strategy_name} (Conf: {confidence:.2%})",
|
| 296 |
'strategy': strategy_name,
|
| 297 |
-
'action_type': action_type,
|
| 298 |
'tp_price': float(tp_price),
|
| 299 |
'sl_price': float(sl_price),
|
| 300 |
'quantile_tp_pct': float(tp_pct),
|
|
@@ -302,7 +294,7 @@ class OracleEngine:
|
|
| 302 |
}
|
| 303 |
|
| 304 |
except Exception as e:
|
| 305 |
-
print(f"❌ [OracleEngine V2.
|
| 306 |
import traceback
|
| 307 |
traceback.print_exc()
|
| 308 |
return {'action': 'WAIT', 'reason': f'Exception: {e}'}
|
|
|
|
| 10 |
# --- [ 0. إعدادات ] ---
|
| 11 |
warnings.filterwarnings('ignore', category=FutureWarning)
|
| 12 |
|
|
|
|
| 13 |
PIPELINE_SETTINGS = {
|
| 14 |
'SWING_PROMINENCE_PCT': 0.02, # 2%
|
| 15 |
}
|
| 16 |
|
|
|
|
|
|
|
| 17 |
DECISION_CONFIDENCE_THRESHOLD = 0.75
|
|
|
|
|
|
|
| 18 |
N_STRATEGY_MODELS = 11
|
| 19 |
|
|
|
|
| 20 |
STRATEGY_MAP = {
|
| 21 |
0: 'WAIT',
|
| 22 |
1: 'SWING_LONG',
|
|
|
|
| 33 |
def __init__(self, model_dir: str = "ml_models/Unified_Models_V1"):
|
| 34 |
"""
|
| 35 |
تهيئة "العقل" الاحتمالي (L3 Oracle).
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
"""
|
| 37 |
self.model_dir = model_dir
|
| 38 |
self.strategy_boosters: List[lgb.Booster] = []
|
|
|
|
| 40 |
|
| 41 |
self.feature_names: List[str] = []
|
| 42 |
self.initialized = False
|
| 43 |
+
# (تحديث الإصدار)
|
| 44 |
+
print("🧠 [OracleEngine V2.2] تم الإنشاء (Actionable-Only Patch). جاهز للتهيئة.")
|
| 45 |
|
| 46 |
async def initialize(self):
|
| 47 |
"""
|
|
|
|
| 50 |
if self.initialized:
|
| 51 |
return True
|
| 52 |
|
| 53 |
+
print(f"🧠 [OracleEngine V2.2] جاري تحميل 15 نموذجاً من {self.model_dir}...")
|
| 54 |
try:
|
| 55 |
# 1. تحميل نماذج "لجنة القرار" (Strategy Ensemble)
|
| 56 |
for i in range(N_STRATEGY_MODELS):
|
|
|
|
| 79 |
self.feature_names = self.strategy_boosters[0].feature_name()
|
| 80 |
self.initialized = True
|
| 81 |
|
| 82 |
+
print(f"✅ [OracleEngine V2.2] جاهز. (Threshold: {DECISION_CONFIDENCE_THRESHOLD*100}%)")
|
| 83 |
print(f" -> سيعمل على الأطر: {TIMEBYTES_TO_PROCESS}")
|
| 84 |
return True
|
| 85 |
|
| 86 |
except Exception as e:
|
| 87 |
+
print(f"❌ [OracleEngine V2.2] فشل فادح أثناء التهيئة: {e}")
|
| 88 |
self.initialized = False
|
| 89 |
return False
|
| 90 |
|
|
|
|
| 128 |
df['volume_zscore'] = (df['volume'] - df['volume'].rolling(50).mean()) / (df['volume'].rolling(50).std() + 1e-9)
|
| 129 |
df['dist_from_EMA200_pct'] = (df['close'] - df['EMA_200']) / (df['EMA_200'] + 1e-9)
|
| 130 |
|
|
|
|
| 131 |
bbu_col = next((col for col in df.columns if 'BBU_20_2.0' in str(col)), None)
|
| 132 |
bbl_col = next((col for col in df.columns if 'BBL_20_2.0' in str(col)), None)
|
| 133 |
bbm_col = next((col for col in df.columns if 'BBM_20_2.0' in str(col)), None)
|
|
|
|
| 161 |
df = self._calculate_fibonacci_matrix(df)
|
| 162 |
df = self._calculate_alpha_strategies(df)
|
| 163 |
|
| 164 |
+
# ملء أي قيم NaN أولية
|
| 165 |
df = df.ffill().bfill()
|
| 166 |
|
| 167 |
# أخذ آخر صف فقط
|
| 168 |
latest_features = df.iloc[-1:]
|
| 169 |
|
|
|
|
| 170 |
try:
|
| 171 |
feature_vector = latest_features[self.feature_names]
|
| 172 |
+
|
| 173 |
+
# --- [ 🔴 🔴 🔴 إصلاح المشكلة 1: NaN Bug ] ---
|
| 174 |
+
# (إصلاح حالات مثل USDC التي ليس لها تقلب)
|
| 175 |
+
feature_vector = feature_vector.fillna(0)
|
| 176 |
+
# --- [ نهاية الإصلاح ] ---
|
| 177 |
+
|
| 178 |
+
# (الفحص الأخير، رغم أن fillna(0) يجب أن تحل المشكلة)
|
| 179 |
if feature_vector.isnull().values.any():
|
| 180 |
+
print("⚠️ [Oracle Warning] Feature vector still contains NaN after fill(0).")
|
| 181 |
return None
|
| 182 |
return feature_vector
|
| 183 |
except Exception as e:
|
|
|
|
| 188 |
async def predict(self, symbol_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 189 |
"""
|
| 190 |
الدالة الرئيسية: تحليل إشارة مرشحة وإرجاع قرار كامل.
|
|
|
|
| 191 |
"""
|
| 192 |
if not self.initialized:
|
| 193 |
return {'action': 'WAIT', 'reason': 'Oracle Engine not initialized'}
|
|
|
|
| 205 |
feature_vector = self._create_feature_vector(ohlcv_data.get(tf))
|
| 206 |
|
| 207 |
if feature_vector is None:
|
| 208 |
+
print(f" -> {symbol_data['symbol']} @ {tf}: Skipped (Insufficient data or NaN)")
|
| 209 |
continue
|
| 210 |
|
|
|
|
| 211 |
all_probs = [
|
| 212 |
booster.predict(feature_vector, num_iteration=booster.best_iteration)
|
| 213 |
for booster in self.strategy_boosters
|
| 214 |
]
|
| 215 |
+
ensemble_probs = np.mean(all_probs, axis=0)[0]
|
| 216 |
+
|
| 217 |
+
# --- [ 🔴 🔴 🔴 إصلاح المشكلة 2: منطق WAIT ] ---
|
| 218 |
+
# (بدلاً من أخذ np.argmax، سنتجاهل الفئة 0)
|
| 219 |
+
|
| 220 |
+
# نسخ الاحتمالات وتعيين احتمالية 'WAIT' (index 0) إلى 0
|
| 221 |
+
actionable_probs = ensemble_probs.copy()
|
| 222 |
+
actionable_probs[0] = 0.0 # (تجاهل WAIT)
|
| 223 |
|
| 224 |
+
# إيجاد أفضل إشارة "قابلة للتنفيذ"
|
| 225 |
+
predicted_strategy_idx = np.argmax(actionable_probs)
|
| 226 |
+
confidence = actionable_probs[predicted_strategy_idx] # (الثقة في أفضل إشارة غير-WAIT)
|
| 227 |
strategy_name = STRATEGY_MAP.get(predicted_strategy_idx, 'WAIT')
|
| 228 |
+
# --- [ نهاية الإصلاح ] ---
|
| 229 |
|
| 230 |
all_tf_decisions.append({
|
| 231 |
'timeframe': tf,
|
| 232 |
'strategy': strategy_name,
|
| 233 |
'confidence': float(confidence),
|
| 234 |
+
'feature_vector': feature_vector
|
| 235 |
})
|
| 236 |
|
| 237 |
if not all_tf_decisions:
|
|
|
|
| 244 |
confidence = best_decision['confidence']
|
| 245 |
best_tf = best_decision['timeframe']
|
| 246 |
|
| 247 |
+
# --- [ الخطوة 3: تطبيق فلتر الثقة (لا تغيير هنا) ] ---
|
|
|
|
| 248 |
if confidence < DECISION_CONFIDENCE_THRESHOLD:
|
| 249 |
return {
|
| 250 |
'action': 'IGNORE',
|
| 251 |
+
'reason': f"Best Actionable Signal ({strategy_name} @ {best_tf}) confidence ({confidence:.2f}) is below threshold ({DECISION_CONFIDENCE_THRESHOLD})",
|
| 252 |
'confidence': confidence,
|
| 253 |
'strategy': strategy_name
|
| 254 |
}
|
| 255 |
|
| 256 |
+
# (الآن نحن متأكدون أن strategy_name ليست 'WAIT' وأن الثقة عالية)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
|
| 258 |
# --- [ الخطوة 4: (نجحت الثقة) - تشغيل "لجنة الأهداف" ] ---
|
| 259 |
winning_feature_vector = best_decision['feature_vector']
|
|
|
|
| 275 |
elif "SHORT" in strategy_name:
|
| 276 |
tp_price = current_price * (1 - tp_pct)
|
| 277 |
sl_price = current_price * (1 + sl_pct)
|
| 278 |
+
action_type = "SELL"
|
| 279 |
else:
|
| 280 |
+
# (هذا مستحيل الحدوث الآن بسبب منطقنا الجديد)
|
| 281 |
return {'action': 'IGNORE', 'reason': 'Strategy not actionable'}
|
| 282 |
|
| 283 |
# --- [ الخطوة 6: إرجاع القرار الكامل ] ---
|
| 284 |
return {
|
| 285 |
+
'action': 'WATCH',
|
| 286 |
'confidence': confidence,
|
| 287 |
'analysis_summary': f"Oracle Consensus @ {best_tf}: {strategy_name} (Conf: {confidence:.2%})",
|
| 288 |
'strategy': strategy_name,
|
| 289 |
+
'action_type': action_type,
|
| 290 |
'tp_price': float(tp_price),
|
| 291 |
'sl_price': float(sl_price),
|
| 292 |
'quantile_tp_pct': float(tp_pct),
|
|
|
|
| 294 |
}
|
| 295 |
|
| 296 |
except Exception as e:
|
| 297 |
+
print(f"❌ [OracleEngine V2.2] فشل فادح أثناء التنبؤ: {e}")
|
| 298 |
import traceback
|
| 299 |
traceback.print_exc()
|
| 300 |
return {'action': 'WAIT', 'reason': f'Exception: {e}'}
|