Riy777 commited on
Commit
e3bba69
·
verified ·
1 Parent(s): 85a5be5

Update ml_engine/oracle_engine.py

Browse files
Files changed (1) hide show
  1. ml_engine/oracle_engine.py +42 -82
ml_engine/oracle_engine.py CHANGED
@@ -10,14 +10,16 @@ from typing import Dict, Any, List, Optional
10
  # --- [ إعدادات النظام ] ---
11
  warnings.filterwarnings('ignore', category=FutureWarning)
12
 
13
- # العتبة الذهبية المستخرجة من اختباراتك (عند 0.65 الدقة ~77%)
14
  CONFIDENCE_THRESHOLD = 0.65
15
 
16
  class OracleEngine:
17
  def __init__(self, model_dir: str = "ml_models/Unified_Models_V1"):
18
  """
19
- Oracle V3.0: The Strategic Brain
20
- يدمج نماذج: Direction (Binary), Target (Multiclass), Strength (Regression)
 
 
21
  """
22
  self.model_dir = model_dir
23
  self.model_direction = None
@@ -26,23 +28,20 @@ class OracleEngine:
26
 
27
  self.feature_cols = []
28
  self.initialized = False
29
- print("🧠 [Oracle V3] Engine Instance Created.")
30
 
31
  async def initialize(self):
32
  """تحميل النماذج وخريطة الميزات"""
33
  if self.initialized: return True
34
 
35
- print(f"🧠 [Oracle V3] Loading artifacts from {self.model_dir}...")
36
  try:
37
- # 1. تحميل خريطة الميزات (لضمان ترتيب الأعمدة)
38
  feat_path = os.path.join(self.model_dir, "feature_columns.pkl")
39
  if not os.path.exists(feat_path):
40
  print(f"❌ [Oracle] Feature map missing: {feat_path}")
41
  return False
42
  self.feature_cols = joblib.load(feat_path)
43
 
44
- # 2. تحميل النماذج
45
- # ملاحظة: تأكد من نقل ملفات .txt من Drive إلى مجلد المشروع المحلي
46
  dir_path = os.path.join(self.model_dir, "lgbm_direction.txt")
47
  tgt_path = os.path.join(self.model_dir, "lgbm_target_class.txt")
48
  str_path = os.path.join(self.model_dir, "lgbm_strength.txt")
@@ -50,7 +49,6 @@ class OracleEngine:
50
  if os.path.exists(dir_path):
51
  self.model_direction = lgb.Booster(model_file=dir_path)
52
  else:
53
- print("❌ [Oracle] Direction Model missing!")
54
  return False
55
 
56
  if os.path.exists(tgt_path):
@@ -60,7 +58,7 @@ class OracleEngine:
60
  self.model_strength = lgb.Booster(model_file=str_path)
61
 
62
  self.initialized = True
63
- print(f"✅ [Oracle V3] Ready. Threshold: {CONFIDENCE_THRESHOLD}")
64
  return True
65
 
66
  except Exception as e:
@@ -68,46 +66,33 @@ class OracleEngine:
68
  return False
69
 
70
  # ==========================================================================
71
- # 🛠️ هندسة الميزات (يجب أن تطابق DataFactory حرفياً)
72
  # ==========================================================================
73
-
74
  def _calculate_snapshot_features(self, df, tf_prefix):
75
- """حساب المؤشرات الفنية المضغوطة"""
76
  df = df.copy()
77
- # تحويلات لضمان الدقة
78
  df['close'] = df['close'].astype(float)
79
  df['volume'] = df['volume'].astype(float)
80
 
81
- # 1. Slope
82
  df[f'{tf_prefix}_slope'] = ta.slope(df['close'], length=7)
83
- # 2. RSI
84
  df[f'{tf_prefix}_rsi'] = ta.rsi(df['close'], length=14)
85
- # 3. ATR Ratio
86
  atr = ta.atr(df['high'], df['low'], df['close'], length=14)
87
  df[f'{tf_prefix}_atr_pct'] = atr / df['close']
88
- # 4. Volume Z-Score
89
  vol_mean = df['volume'].rolling(20).mean()
90
  vol_std = df['volume'].rolling(20).std()
91
  df[f'{tf_prefix}_vol_z'] = (df['volume'] - vol_mean) / (vol_std + 1e-9)
92
 
93
- # إرجاع الأعمدة فقط (مع ملء الفراغات للحسابات اللحظية)
94
  cols = [f'{tf_prefix}_slope', f'{tf_prefix}_rsi', f'{tf_prefix}_atr_pct', f'{tf_prefix}_vol_z']
95
  return df[cols].ffill().bfill()
96
 
97
  def _create_feature_vector(self, ohlcv_data: Dict[str, Any], titan_score: float, mc_score: float, pattern_score: float) -> Optional[pd.DataFrame]:
98
- """تجميع متجه الميزات ومحاكاة مدخلات الطبقة الثانية"""
99
  try:
100
- # 1. التحقق من البيانات
101
  raw_1h = ohlcv_data.get('1h')
102
  if not raw_1h or len(raw_1h) < 30: return None
103
 
104
- # تحويل البيانات إلى DataFrame
105
  df_1h = pd.DataFrame(raw_1h, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
106
  df_15m = pd.DataFrame(ohlcv_data.get('15m', []), columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
107
  df_4h = pd.DataFrame(ohlcv_data.get('4h', []), columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
108
 
109
- # 2. حساب الميزات الفنية (Snapshot)
110
- # نأخذ آخر صف فقط (State الحالية)
111
  feats_1h = self._calculate_snapshot_features(df_1h, "1h").iloc[-1:].reset_index(drop=True)
112
 
113
  if len(df_15m) > 20:
@@ -120,97 +105,81 @@ class OracleEngine:
120
  else:
121
  feats_4h = pd.DataFrame(np.zeros((1, 4)), columns=[f'4h_{c}' for c in ['slope', 'rsi', 'atr_pct', 'vol_z']])
122
 
123
- # 3. التجميع (Vector Assembly)
124
  vector = pd.concat([feats_1h, feats_15m, feats_4h], axis=1)
125
-
126
- # 4. حقن درجات الطبقة الثانية (Injection)
127
- # هنا نربط الدرجات الحقيقية بالأعمدة التي تدرب عليها النموذج (sim_...)
128
  vector['sim_titan_score'] = float(titan_score)
129
  vector['sim_mc_score'] = float(mc_score)
130
  vector['sim_pattern_score'] = float(pattern_score)
131
 
132
- # 5. محاذاة الأعمدة (Column Alignment)
133
- # يجب أن نرسل للنموذج نفس الأعمدة بنفس الترتيب
134
  final_vector = pd.DataFrame(columns=self.feature_cols)
135
  for col in self.feature_cols:
136
  if col in vector.columns:
137
  final_vector.at[0, col] = vector[col].iloc[0]
138
  else:
139
- final_vector.at[0, col] = 0.0 # Missing features default to 0
140
 
141
  return final_vector.astype(float)
142
 
143
  except Exception as e:
144
- print(f"⚠️ [Oracle] Vector build failed: {e}")
145
  return None
146
 
147
  # ==========================================================================
148
- # 🔮 التنبؤ (Inference Logic)
149
  # ==========================================================================
150
-
151
  async def predict(self, symbol_data: Dict[str, Any]) -> Dict[str, Any]:
152
- """تحليل الفرصة وإصدار التوصية"""
153
  if not self.initialized:
154
  return {'action': 'WAIT', 'reason': 'Not initialized'}
155
 
156
  try:
157
- # استلام البيانات
158
  ohlcv = symbol_data.get('ohlcv')
159
  current_price = symbol_data.get('current_price', 0.0)
160
-
161
- # الدرجات القادمة من L2
162
  titan = symbol_data.get('titan_score', 0.5)
163
  mc = symbol_data.get('mc_score', 0.5)
164
  patt = symbol_data.get('patterns_score', 0.5)
165
 
166
- # 1. بناء المتجه
167
  features = self._create_feature_vector(ohlcv, titan, mc, patt)
168
  if features is None:
169
  return {'action': 'WAIT', 'reason': 'Features failed'}
170
 
171
- # 2. التنبؤ بالاتجاه (Direction)
172
- # النموذج يعيد احتمالية لكل كلاس. بما أننا دربنا 1=Long, 2=Short
173
- # LightGBM (Multi-class) يعيد array: [Prob_Wait(محذوف عملياً), Prob_Long, Prob_Short]
174
- # أو إذا كان Binary (0=Long, 1=Short) يعيد احتمالية Short.
175
- # *تذكير*: في كود التدريب الأخير استخدمنا: y - 1. إذن: 0=Long, 1=Short.
176
 
177
- dir_probs = self.model_direction.predict(features)[0] # Array [Prob_Long, Prob_Short]
178
-
179
- # التعامل مع نوع المخرجات (حسب نسخة LightGBM)
180
  if isinstance(dir_probs, (np.ndarray, list)):
181
  prob_long = dir_probs[0]
182
  prob_short = dir_probs[1]
183
  else:
184
- # Binary objective case
185
  prob_short = dir_probs
186
  prob_long = 1.0 - dir_probs
187
 
188
- # تحديد الاتجاه الأقوى
189
- if prob_long > prob_short:
190
- direction = "LONG"
191
- confidence = prob_long
192
- else:
193
- direction = "SHORT"
194
- confidence = prob_short
 
 
 
 
 
195
 
196
- # 3. البوابة المنطقية (Threshold Check)
197
  if confidence < CONFIDENCE_THRESHOLD:
198
  return {
199
  'action': 'WAIT',
200
- 'reason': f'Low Confidence ({confidence:.2f} < {CONFIDENCE_THRESHOLD})',
201
- 'direction': direction
202
  }
203
 
204
- # 4. التنبؤ بالأهداف والقوة (فقط للفرص المؤكدة)
205
-
206
- # Strength (Regression: 0.0 - 1.0)
207
  strength = 0.5
208
  if self.model_strength:
209
  strength = float(self.model_strength.predict(features)[0])
210
- strength = max(0.0, min(1.0, strength)) # Clip
211
 
212
- # Target Class (0=TP1, 1=TP2, 2=TP3, 3=TP4)
213
- tp_class_idx = 1 # Default TP2
214
  if self.model_target:
215
  tgt_probs = self.model_target.predict(features)[0]
216
  tp_class_idx = np.argmax(tgt_probs)
@@ -218,41 +187,32 @@ class OracleEngine:
218
  tp_labels = ['TP1', 'TP2', 'TP3', 'TP4']
219
  target_profile = tp_labels[tp_class_idx]
220
 
221
- # 5. حساب المستويات السعرية (ATR-Based)
222
- # نحتاج قيمة ATR الحالية
223
  atr_pct_val = features['1h_atr_pct'].iloc[0]
224
  atr_abs = atr_pct_val * current_price
225
 
226
- dir_mult = 1 if direction == "LONG" else -1
227
-
228
- # خريطة الأهداف
229
  tp_map = {
230
- 'TP1': current_price + (dir_mult * 0.8 * atr_abs),
231
- 'TP2': current_price + (dir_mult * 1.5 * atr_abs),
232
- 'TP3': current_price + (dir_mult * 2.5 * atr_abs),
233
- 'TP4': current_price + (dir_mult * 4.0 * atr_abs),
234
  }
235
 
236
- # الهدف الأساسي الموصى به
237
  primary_tp = tp_map[target_profile]
238
-
239
- # الوقف (Stop Loss) - دائماً 1.2 ATR كبداية
240
- sl_price = current_price - (dir_mult * 1.2 * atr_abs)
241
 
242
  return {
243
- 'action': 'WATCH', # إشارة صالحة للمتابعة
244
- 'action_type': 'BUY' if direction == "LONG" else 'SELL',
245
  'confidence': float(confidence),
246
  'strength': float(strength),
247
  'target_class': target_profile,
248
  'primary_tp': float(primary_tp),
249
  'sl_price': float(sl_price),
250
  'tp_map': tp_map,
251
- 'analysis_summary': f"{direction} (Conf: {confidence:.0%}) | Strength: {strength:.2f} | Aiming: {target_profile}"
252
  }
253
 
254
  except Exception as e:
255
  print(f"❌ [Oracle] Prediction Error: {e}")
256
- import traceback
257
- traceback.print_exc()
258
  return {'action': 'WAIT', 'reason': 'Error'}
 
10
  # --- [ إعدادات النظام ] ---
11
  warnings.filterwarnings('ignore', category=FutureWarning)
12
 
13
+ # العتبة الذهبية (للدخول شراء فقط)
14
  CONFIDENCE_THRESHOLD = 0.65
15
 
16
  class OracleEngine:
17
  def __init__(self, model_dir: str = "ml_models/Unified_Models_V1"):
18
  """
19
+ Oracle V4.0: Spot-Only Strategic Brain
20
+ - يحلل الاتجاه (صعود/هبوط).
21
+ - يمرر فقط فرص الصعود (Long) للتنفيذ.
22
+ - يحجب فرص الهبوط (Short) ويعتبرها "مخاطرة" (WAIT).
23
  """
24
  self.model_dir = model_dir
25
  self.model_direction = None
 
28
 
29
  self.feature_cols = []
30
  self.initialized = False
31
+ print("🧠 [Oracle V4 - Spot] Engine Instance Created.")
32
 
33
  async def initialize(self):
34
  """تحميل النماذج وخريطة الميزات"""
35
  if self.initialized: return True
36
 
37
+ print(f"🧠 [Oracle V4] Loading artifacts from {self.model_dir}...")
38
  try:
 
39
  feat_path = os.path.join(self.model_dir, "feature_columns.pkl")
40
  if not os.path.exists(feat_path):
41
  print(f"❌ [Oracle] Feature map missing: {feat_path}")
42
  return False
43
  self.feature_cols = joblib.load(feat_path)
44
 
 
 
45
  dir_path = os.path.join(self.model_dir, "lgbm_direction.txt")
46
  tgt_path = os.path.join(self.model_dir, "lgbm_target_class.txt")
47
  str_path = os.path.join(self.model_dir, "lgbm_strength.txt")
 
49
  if os.path.exists(dir_path):
50
  self.model_direction = lgb.Booster(model_file=dir_path)
51
  else:
 
52
  return False
53
 
54
  if os.path.exists(tgt_path):
 
58
  self.model_strength = lgb.Booster(model_file=str_path)
59
 
60
  self.initialized = True
61
+ print(f"✅ [Oracle V4] Ready (Spot Mode). Threshold: {CONFIDENCE_THRESHOLD}")
62
  return True
63
 
64
  except Exception as e:
 
66
  return False
67
 
68
  # ==========================================================================
69
+ # 🛠️ هندسة الميزات (مطابقة لـ DataFactory)
70
  # ==========================================================================
 
71
  def _calculate_snapshot_features(self, df, tf_prefix):
 
72
  df = df.copy()
 
73
  df['close'] = df['close'].astype(float)
74
  df['volume'] = df['volume'].astype(float)
75
 
 
76
  df[f'{tf_prefix}_slope'] = ta.slope(df['close'], length=7)
 
77
  df[f'{tf_prefix}_rsi'] = ta.rsi(df['close'], length=14)
 
78
  atr = ta.atr(df['high'], df['low'], df['close'], length=14)
79
  df[f'{tf_prefix}_atr_pct'] = atr / df['close']
 
80
  vol_mean = df['volume'].rolling(20).mean()
81
  vol_std = df['volume'].rolling(20).std()
82
  df[f'{tf_prefix}_vol_z'] = (df['volume'] - vol_mean) / (vol_std + 1e-9)
83
 
 
84
  cols = [f'{tf_prefix}_slope', f'{tf_prefix}_rsi', f'{tf_prefix}_atr_pct', f'{tf_prefix}_vol_z']
85
  return df[cols].ffill().bfill()
86
 
87
  def _create_feature_vector(self, ohlcv_data: Dict[str, Any], titan_score: float, mc_score: float, pattern_score: float) -> Optional[pd.DataFrame]:
 
88
  try:
 
89
  raw_1h = ohlcv_data.get('1h')
90
  if not raw_1h or len(raw_1h) < 30: return None
91
 
 
92
  df_1h = pd.DataFrame(raw_1h, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
93
  df_15m = pd.DataFrame(ohlcv_data.get('15m', []), columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
94
  df_4h = pd.DataFrame(ohlcv_data.get('4h', []), columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
95
 
 
 
96
  feats_1h = self._calculate_snapshot_features(df_1h, "1h").iloc[-1:].reset_index(drop=True)
97
 
98
  if len(df_15m) > 20:
 
105
  else:
106
  feats_4h = pd.DataFrame(np.zeros((1, 4)), columns=[f'4h_{c}' for c in ['slope', 'rsi', 'atr_pct', 'vol_z']])
107
 
 
108
  vector = pd.concat([feats_1h, feats_15m, feats_4h], axis=1)
 
 
 
109
  vector['sim_titan_score'] = float(titan_score)
110
  vector['sim_mc_score'] = float(mc_score)
111
  vector['sim_pattern_score'] = float(pattern_score)
112
 
 
 
113
  final_vector = pd.DataFrame(columns=self.feature_cols)
114
  for col in self.feature_cols:
115
  if col in vector.columns:
116
  final_vector.at[0, col] = vector[col].iloc[0]
117
  else:
118
+ final_vector.at[0, col] = 0.0
119
 
120
  return final_vector.astype(float)
121
 
122
  except Exception as e:
 
123
  return None
124
 
125
  # ==========================================================================
126
+ # 🔮 التنبؤ (Inference Logic - SPOT MODE)
127
  # ==========================================================================
 
128
  async def predict(self, symbol_data: Dict[str, Any]) -> Dict[str, Any]:
129
+ """تحليل الفرصة: هل هي صالحة للشراء (SPOT)؟"""
130
  if not self.initialized:
131
  return {'action': 'WAIT', 'reason': 'Not initialized'}
132
 
133
  try:
 
134
  ohlcv = symbol_data.get('ohlcv')
135
  current_price = symbol_data.get('current_price', 0.0)
 
 
136
  titan = symbol_data.get('titan_score', 0.5)
137
  mc = symbol_data.get('mc_score', 0.5)
138
  patt = symbol_data.get('patterns_score', 0.5)
139
 
 
140
  features = self._create_feature_vector(ohlcv, titan, mc, patt)
141
  if features is None:
142
  return {'action': 'WAIT', 'reason': 'Features failed'}
143
 
144
+ # 1. التنبؤ بالاتجاه (Direction)
145
+ # 0=Long (Buy), 1=Short (Drop/Avoid)
146
+ dir_probs = self.model_direction.predict(features)[0]
 
 
147
 
 
 
 
148
  if isinstance(dir_probs, (np.ndarray, list)):
149
  prob_long = dir_probs[0]
150
  prob_short = dir_probs[1]
151
  else:
 
152
  prob_short = dir_probs
153
  prob_long = 1.0 - dir_probs
154
 
155
+ # --- [SPOT LOGIC ENFORCEMENT] ---
156
+ # إذا كان احتمال الهبوط (Short) أعلى، فهذا يعني "تجنب العملة".
157
+ # لا نفتح صفقة Short، بل نقول WAIT.
158
+ if prob_short > prob_long:
159
+ return {
160
+ 'action': 'WAIT',
161
+ 'reason': f'Bearish Forecast (Short Prob: {prob_short:.2f})',
162
+ 'direction': 'SHORT' # For debugging only
163
+ }
164
+
165
+ # إذا وصلنا هنا، فالاتجاه هو LONG (شراء)
166
+ confidence = prob_long
167
 
168
+ # 2. البوابة المنطقية (Threshold Check)
169
  if confidence < CONFIDENCE_THRESHOLD:
170
  return {
171
  'action': 'WAIT',
172
+ 'reason': f'Low Buy Confidence ({confidence:.2f})',
173
+ 'direction': 'LONG'
174
  }
175
 
176
+ # 3. التنبؤ بالأهداف والقوة
 
 
177
  strength = 0.5
178
  if self.model_strength:
179
  strength = float(self.model_strength.predict(features)[0])
180
+ strength = max(0.0, min(1.0, strength))
181
 
182
+ tp_class_idx = 1
 
183
  if self.model_target:
184
  tgt_probs = self.model_target.predict(features)[0]
185
  tp_class_idx = np.argmax(tgt_probs)
 
187
  tp_labels = ['TP1', 'TP2', 'TP3', 'TP4']
188
  target_profile = tp_labels[tp_class_idx]
189
 
190
+ # 4. حساب المستويات السعرية (للاتجاه الصاعد فقط)
 
191
  atr_pct_val = features['1h_atr_pct'].iloc[0]
192
  atr_abs = atr_pct_val * current_price
193
 
 
 
 
194
  tp_map = {
195
+ 'TP1': current_price + (1.0 * atr_abs),
196
+ 'TP2': current_price + (1.8 * atr_abs),
197
+ 'TP3': current_price + (2.8 * atr_abs),
198
+ 'TP4': current_price + (4.5 * atr_abs),
199
  }
200
 
 
201
  primary_tp = tp_map[target_profile]
202
+ sl_price = current_price - (1.2 * atr_abs) # وقف الخسارة تحت السعر
 
 
203
 
204
  return {
205
+ 'action': 'WATCH', # إشارة شراء صالحة
206
+ 'action_type': 'BUY', # دائماً BUY في Spot
207
  'confidence': float(confidence),
208
  'strength': float(strength),
209
  'target_class': target_profile,
210
  'primary_tp': float(primary_tp),
211
  'sl_price': float(sl_price),
212
  'tp_map': tp_map,
213
+ 'analysis_summary': f"SPOT BUY (Conf: {confidence:.0%}) | Str: {strength:.2f} | Aim: {target_profile}"
214
  }
215
 
216
  except Exception as e:
217
  print(f"❌ [Oracle] Prediction Error: {e}")
 
 
218
  return {'action': 'WAIT', 'reason': 'Error'}