Riy777 commited on
Commit
2f902f0
·
verified ·
1 Parent(s): 5730dd9

Update ml_engine/hybrid_guardian.py

Browse files
Files changed (1) hide show
  1. ml_engine/hybrid_guardian.py +114 -172
ml_engine/hybrid_guardian.py CHANGED
@@ -1,6 +1,5 @@
1
  # ml_engine/hybrid_guardian.py
2
- # (V4.0 - The Dual Brain: Hybrid V2 + V3 Logic - FULL PRODUCTION CODE)
3
- # GEM-Architect Implementation
4
 
5
  import os
6
  import json
@@ -15,6 +14,7 @@ class HybridDeepSteward:
15
  def __init__(self, v2_model_path, v3_model_path, v3_features_map_path):
16
  """
17
  The Hybrid Guardian: Combines V2 (Regime Detection) and V3 (Precision Exit).
 
18
  """
19
  self.v2_path = v2_model_path
20
  self.v3_path = v3_model_path
@@ -25,16 +25,19 @@ class HybridDeepSteward:
25
  self.v3_feature_names = []
26
  self.initialized = False
27
 
28
- # ⚙️ إعدادات الإستراتيجية الهجينة
29
- # 1. V2 Gates (البوابات - رادار السياق)
30
- self.V2_RISK_GATE = 0.60 # إذا كان V2 أقل من هذا، نتجاهل V3 (وضع آمن)
31
- self.V2_PANIC_TRIGGER = 0.90 # إذا وصل V2 لهذا، نخرج فوراً (Black Swan)
32
 
33
- # 2. V3 Thresholds (القناص - القرار اللحظي)
34
- self.V3_HARD_EXIT = 0.90 # خروج كامل
35
- self.V3_SOFT_EXIT = 0.75 # خروج جزئي أو شد الستوب (تحذير)
 
36
 
37
- # ميزات V2 (ثابتة من التصميم القديم - تتطلب 64 شمعة)
 
 
 
 
 
38
  self.V2_FEATURES = [
39
  'log_ret', 'rel_vol', 'rsi_norm', 'macd_hist', 'roc',
40
  'bb_width', 'bb_pct', 'atr_pct', 'dist_ema50', 'dist_ema200',
@@ -42,134 +45,93 @@ class HybridDeepSteward:
42
  ]
43
 
44
  def initialize(self):
45
- """تحميل كلا النموذجين"""
46
  try:
47
- if not os.path.exists(self.v2_path):
48
- print(f"❌ [HybridGuardian] V2 Model missing: {self.v2_path}")
49
- return False
50
- if not os.path.exists(self.v3_path):
51
- print(f"❌ [HybridGuardian] V3 Model missing: {self.v3_path}")
52
- return False
53
 
54
- # Load V2
55
  self.model_v2 = xgb.Booster()
56
  self.model_v2.load_model(self.v2_path)
57
 
58
- # Load V3
59
  self.model_v3 = xgb.Booster()
60
  self.model_v3.load_model(self.v3_path)
61
 
62
- # Load V3 Features Map
63
  if os.path.exists(self.v3_features_path):
64
  with open(self.v3_features_path, 'r') as f:
65
  self.v3_feature_names = json.load(f)
66
  else:
67
- print("❌ [HybridGuardian] V3 Feature map missing.")
68
  return False
69
 
70
  self.initialized = True
71
- print(f"✅ [HybridGuardian] Active. V2 Gate: {self.V2_RISK_GATE}, V3 Hard: {self.V3_HARD_EXIT}")
72
  return True
73
 
74
  except Exception as e:
75
  print(f"❌ [HybridGuardian] Init Failed: {e}")
76
- traceback.print_exc()
77
  return False
78
 
79
  # ==========================================================================
80
  # 🧠 V2 Feature Logic (Legacy 64-Candle Sequence)
81
  # ==========================================================================
82
  def _engineer_v2_features(self, df, current_price, entry_price):
83
- """
84
- تحضير ميزات النموذج القديم V2.
85
- يتطلب هذا النموذج تسلسل زمني (Sequence) لآخر 64 شمعة، ثم تسطيحها.
86
- """
87
  try:
88
  df = df.copy()
89
- # نحتاج على الأقل 200 شمعة لحساب المؤشرات (مثل EMA200) بشكل صحيح قبل القص
90
  if len(df) < 200: return None
91
 
92
- # 1. الحسابات الأساسية
93
  df['log_ret'] = np.log(df['close'] / df['close'].shift(1))
94
-
95
  vol_sma = df['volume'].rolling(window=50).mean()
96
  df['rel_vol'] = (df['volume'] - vol_sma) / (vol_sma + 1e-9)
97
-
98
  df['rsi'] = ta.rsi(df['close'], length=14)
99
  df['rsi_norm'] = df['rsi'] / 100.0
100
-
101
  macd = ta.macd(df['close'], fast=12, slow=26, signal=9)
102
- # pandas_ta returns MACDh_12_26_9
103
  hist_col = [c for c in macd.columns if 'MACDh' in c][0]
104
  df['macd_hist'] = macd[hist_col]
105
-
106
  df['roc'] = ta.roc(df['close'], length=9) / 100.0
107
-
108
  bb = ta.bbands(df['close'], length=20, std=2)
109
  w_col = [c for c in bb.columns if 'BBB' in c][0]
110
  p_col = [c for c in bb.columns if 'BBP' in c][0]
111
  df['bb_width'] = bb[w_col] / 100.0
112
  df['bb_pct'] = bb[p_col]
113
-
114
  df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
115
  df['atr_pct'] = df['atr'] / df['close']
116
-
117
  df['ema50'] = ta.ema(df['close'], length=50)
118
  df['ema200'] = ta.ema(df['close'], length=200)
119
  df['dist_ema50'] = (df['close'] - df['ema50']) / (df['ema50'] + 1e-9)
120
  df['dist_ema200'] = (df['close'] - df['ema200']) / (df['ema200'] + 1e-9)
121
-
122
- # القناة السعرية للصفقة الحالية
123
  current_pnl = (current_price - entry_price) / entry_price
124
  df['pnl_channel'] = current_pnl
125
 
126
  df = df.fillna(0)
127
-
128
- # 2. استخراج آخر 64 شمعة (Sequence)
129
- window = df.iloc[-64:][self.V2_FEATURES].values # Shape: (64, 11)
130
-
131
- if window.shape[0] < 64:
132
- return None
133
-
134
- # 3. التسطيح (Flattening) ليصبح (1, 704)
135
- feature_vector = window.reshape(1, -1)
136
- return feature_vector
137
-
138
- except Exception as e:
139
- # print(f"V2 Feature Error: {e}")
140
  return None
141
 
142
  # ==========================================================================
143
- # 🧬 V3 Feature Logic (Advanced Multi-Timeframe Single Row)
144
  # ==========================================================================
145
  def _add_v3_indicators(self, df, window_mc):
146
- """ دالة مساعدة لحساب مؤشرات V3 ومونت كارلو """
147
  try:
148
- # 1. Technicals
149
  df['rsi'] = ta.rsi(df['close'], length=14)
150
  df['rsi_slope'] = ta.slope(df['rsi'], length=3)
151
-
152
  macd = ta.macd(df['close'])
153
  if macd is not None and 'MACDh_12_26_9' in macd.columns:
154
  df['macd_h'] = macd['MACDh_12_26_9']
155
  df['macd_h_slope'] = ta.slope(df['macd_h'], length=3)
156
  else:
157
- df['macd_h'] = 0
158
- df['macd_h_slope'] = 0
159
 
160
  adx = ta.adx(df['high'], df['low'], df['close'], length=14)
161
  if adx is not None and 'ADX_14' in adx.columns:
162
  df['adx'] = adx['ADX_14']
163
  df['trend_net_force'] = adx['DMP_14'] - adx['DMN_14']
164
  else:
165
- df['adx'] = 0
166
- df['trend_net_force'] = 0
167
 
168
- # EMAs
169
  df['ema_20'] = ta.ema(df['close'], length=20)
170
  df['ema_50'] = ta.ema(df['close'], length=50)
171
  df['ema_200'] = ta.ema(df['close'], length=200)
172
-
173
  df['dist_ema20'] = (df['close'] - df['ema_20']) / df['close']
174
  df['dist_ema50'] = (df['close'] - df['ema_50']) / df['close']
175
  df['dist_ema200'] = (df['close'] - df['ema_200']) / df['close']
@@ -183,177 +145,157 @@ class HybridDeepSteward:
183
  df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
184
  df['atr_rel'] = df['atr'] / df['close']
185
 
186
- # Volume
187
  df['obv'] = ta.obv(df['close'], df['volume'])
188
  df['obv_slope'] = ta.slope(df['obv'], length=5)
189
  df['cmf'] = ta.cmf(df['high'], df['low'], df['close'], df['volume'], length=20)
190
 
191
- # 2. Monte Carlo (Vectorized)
192
  df['log_ret'] = np.log(df['close'] / df['close'].shift(1))
193
-
194
  roll_std = df['log_ret'].rolling(window=window_mc).std()
195
  roll_mean = df['log_ret'].rolling(window=window_mc).mean()
196
-
197
  df['mc_skew'] = df['log_ret'].rolling(window=window_mc*2).skew()
198
  df['mc_kurt'] = df['log_ret'].rolling(window=window_mc*2).kurt()
199
-
200
- # Probability
201
  drift = roll_mean - (0.5 * roll_std**2)
202
  z_score = (0 - drift) / (roll_std + 1e-9)
203
  df['mc_prob_gain'] = 1 - norm.cdf(z_score)
204
-
205
  var_95 = drift + (-1.645 * roll_std)
206
  df['mc_var_95'] = var_95
207
  df['mc_shock'] = (df['log_ret'] - roll_mean) / (roll_std + 1e-9)
208
 
209
  return df
210
- except Exception as e:
211
- # print(f"V3 Indicator Error: {e}")
212
  return df
213
 
214
  def _engineer_v3_features(self, ohlcv_1m, ohlcv_5m, ohlcv_15m):
215
- """
216
- تحضير ميزات النموذج الجديد V3.
217
- يعتمد على آخر صف فقط (Last Row) بعد دمج المؤشرات من الأطر الزمنية المختلفة.
218
- """
219
  try:
220
- # 1. Convert to DF
221
  df1 = pd.DataFrame(ohlcv_1m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
222
  df5 = pd.DataFrame(ohlcv_5m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
223
  df15 = pd.DataFrame(ohlcv_15m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
224
 
225
- # التأكد من كفاية البيانات (للمتوسطات المتحركة الطويلة)
226
- if len(df1) < 200 or len(df5) < 50 or len(df15) < 50:
227
- return None
228
 
229
- # 2. Calculate Indicators (Full logic included locally)
230
  df1 = self._add_v3_indicators(df1, window_mc=30)
231
  df5 = self._add_v3_indicators(df5, window_mc=30)
232
  df15 = self._add_v3_indicators(df15, window_mc=20)
233
 
234
- # 3. Merge Logic (Extracting Last Row Context)
235
- # بما أننا نستخدم البيانات في اللحظة الحالية (Inference)، لا نحتاج لـ merge_asof المعقدة للماضي،
236
- # بل نحتاج لآخر قيمة معروفة من كل إطار زمني.
237
-
238
- row1 = df1.iloc[-1]
239
- row5 = df5.iloc[-1]
240
- row15 = df15.iloc[-1]
241
 
242
- combined_features = {}
243
-
244
- # 1m Features (Primary)
245
- for col in row1.index:
246
- combined_features[col] = row1[col]
 
247
 
248
- # 5m Features (Context - with suffix)
249
- # القائمة يجب أن تطابق ما تم استخدامه في دالة process_symbol_data أثناء هندسة الميزات
250
- cols_5m_target = ['rsi', 'rsi_slope', 'macd_h', 'bb_pct', 'mc_prob_gain', 'mc_shock']
251
- for col in cols_5m_target:
252
- if col in row5:
253
- combined_features[f"{col}_5m"] = row5[col]
254
-
255
- # 15m Features (Trend - with suffix)
256
- cols_15m_target = ['rsi', 'macd_h', 'trend_net_force', 'mc_prob_gain', 'dist_ema200']
257
- for col in cols_15m_target:
258
- if col in row15:
259
- combined_features[f"{col}_15m"] = row15[col]
260
-
261
- # 4. Align with Feature Map (The crucial step)
262
- # ترتيب الأعمدة يجب أن يكون مطابقاً تماماً لملف JSON
263
  vector = []
264
  for fname in self.v3_feature_names:
265
- val = combined_features.get(fname, np.nan)
266
-
267
- # معالجة القيم المفقودة (نفس منطق XGBoost الافتراضي أو صفر)
268
- if pd.isna(val) or np.isinf(val):
269
- val = 0.0
270
  vector.append(val)
271
 
272
- # Reshape for prediction (1 sample, N features)
273
  return np.array(vector).reshape(1, -1)
274
-
275
- except Exception as e:
276
- print(f"V3 Feature Prep Error: {e}")
277
- traceback.print_exc()
278
  return None
279
 
280
  # ==========================================================================
281
- # 🛡️ The Grand Logic (التنفيذ الهجين)
282
  # ==========================================================================
283
  def analyze_position(self, ohlcv_1m, ohlcv_5m, ohlcv_15m, entry_price):
284
- """
285
- يحلل الصفقة باستخدام النموذجين معاً.
286
- """
287
  if not self.initialized:
288
  return {'action': 'HOLD', 'reason': 'Not Initialized'}
289
 
290
  try:
291
- # 1. تحليل V2 (الرادار - يحتاج 64 شمعة دقيقة)
292
  df_1m_raw = pd.DataFrame(ohlcv_1m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
293
  current_price = df_1m_raw['close'].iloc[-1]
294
 
 
295
  feat_v2 = self._engineer_v2_features(df_1m_raw, current_price, entry_price)
296
-
297
- if feat_v2 is None:
298
- score_v2 = 0.0 # بيانات غير كافية -> افترض الأمان
299
- else:
300
- # V2 يعطي احتمالية الخطر
301
- score_v2 = float(self.model_v2.predict(xgb.DMatrix(feat_v2))[0])
302
 
303
- # 2. تحليل V3 (القناص - يحتاج دمج الأطر)
304
  feat_v3 = self._engineer_v3_features(ohlcv_1m, ohlcv_5m, ohlcv_15m)
305
-
306
- if feat_v3 is None:
307
- score_v3 = 0.0
308
- else:
309
- # V3 يعطي احتمالية الخطر بدقة
310
- score_v3 = float(self.model_v3.predict(xgb.DMatrix(feat_v3, feature_names=self.v3_feature_names))[0])
311
 
312
- # 3. منطق القرار (The Gating Logic)
313
-
314
- # السيناريو أ: انهيار كارثي يكتشفه V2 (Black Swan)
 
 
 
 
315
  if score_v2 >= self.V2_PANIC_TRIGGER:
316
  return {
317
  'action': 'EXIT_HARD',
318
  'confidence': score_v2,
319
- 'reason': f'🚨 V2 PANIC (Score: {score_v2:.2f}) - Market Crash Detected',
320
- 'scores': {'v2': score_v2, 'v3': score_v3}
321
  }
322
 
323
- # السيناريو ب: السوق آمن حسب V2 (تجاهل V3 لتقليل الضوضاء)
324
- if score_v2 < self.V2_RISK_GATE:
325
- return {
326
- 'action': 'HOLD',
327
- 'confidence': 1.0 - score_v2, # ثقة البقاء
328
- 'reason': f'✅ V2 Safe Zone (Score: {score_v2:.2f})',
329
- 'scores': {'v2': score_v2, 'v3': score_v3}
330
- }
 
 
 
 
 
 
 
 
 
331
 
332
- # السيناريو ج: منطقة القلق (V2 > 0.60) -> استشر القناص V3
333
- if score_v3 >= self.V3_HARD_EXIT:
334
- return {
335
- 'action': 'EXIT_HARD',
336
- 'confidence': score_v3,
337
- 'reason': f'🎯 V3 Sniper Kill (Score: {score_v3:.2f}) [V2 Activated]',
338
- 'scores': {'v2': score_v2, 'v3': score_v3}
339
- }
340
-
341
- elif score_v3 >= self.V3_SOFT_EXIT:
342
- return {
343
- 'action': 'EXIT_SOFT', # أو Tighten SL
344
- 'confidence': score_v3,
345
- 'reason': f'⚠️ V3 Warning (Score: {score_v3:.2f}) [V2 Activated]',
346
- 'scores': {'v2': score_v2, 'v3': score_v3}
347
- }
 
 
 
 
 
 
 
348
 
349
- else:
350
- # V2 قلق، لكن V3 لم يجد سبباً تقنياً محدداً للخروج
351
- return {
352
- 'action': 'HOLD',
353
- 'confidence': 1.0 - score_v3,
354
- 'reason': f'👀 V2 Alert ({score_v2:.2f}) but V3 holds ({score_v3:.2f})',
355
- 'scores': {'v2': score_v2, 'v3': score_v3}
356
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
 
358
  except Exception as e:
359
  print(f"❌ [HybridGuardian] Inference Error: {e}")
 
1
  # ml_engine/hybrid_guardian.py
2
+ # (V23.0 - GEM-Architect: Institutional Fuzzy Logic + Ultra-Confidence Override)
 
3
 
4
  import os
5
  import json
 
14
  def __init__(self, v2_model_path, v3_model_path, v3_features_map_path):
15
  """
16
  The Hybrid Guardian: Combines V2 (Regime Detection) and V3 (Precision Exit).
17
+ Uses "Fuzzy Logic" zones instead of hard cutoffs.
18
  """
19
  self.v2_path = v2_model_path
20
  self.v3_path = v3_model_path
 
25
  self.v3_feature_names = []
26
  self.initialized = False
27
 
28
+ # ⚙️ إعدادات المناطق المنطقية (Institutional Logic Zones)
 
 
 
29
 
30
+ # 1. حدود V2 (الرادار - يحدد السياق)
31
+ self.V2_SAFE_LIMIT = 0.50 # تحت هذا الرقم: أمان قوي
32
+ self.V2_GREY_LIMIT = 0.60 # بين 0.50 و 0.60: منطقة رمادية (قلق بسيط)
33
+ self.V2_PANIC_TRIGGER = 0.90 # فوق هذا الرقم: انهيار مؤكد (Panic)
34
 
35
+ # 2. حدود V3 لقناص - يحدد التنفيذ)
36
+ self.V3_SOFT_EXIT = 0.75 # خروج جزئي/تحذير
37
+ self.V3_HARD_EXIT = 0.90 # خروج كامل قياسي
38
+ self.V3_ULTRA_CONF = 0.97 # "كسر الفيتو": خروج حتى لو V2 آمن
39
+
40
+ # ميزات V2 (ثابتة)
41
  self.V2_FEATURES = [
42
  'log_ret', 'rel_vol', 'rsi_norm', 'macd_hist', 'roc',
43
  'bb_width', 'bb_pct', 'atr_pct', 'dist_ema50', 'dist_ema200',
 
45
  ]
46
 
47
  def initialize(self):
48
+ """تحميل النماذج"""
49
  try:
50
+ if not os.path.exists(self.v2_path): return False
51
+ if not os.path.exists(self.v3_path): return False
 
 
 
 
52
 
 
53
  self.model_v2 = xgb.Booster()
54
  self.model_v2.load_model(self.v2_path)
55
 
 
56
  self.model_v3 = xgb.Booster()
57
  self.model_v3.load_model(self.v3_path)
58
 
 
59
  if os.path.exists(self.v3_features_path):
60
  with open(self.v3_features_path, 'r') as f:
61
  self.v3_feature_names = json.load(f)
62
  else:
 
63
  return False
64
 
65
  self.initialized = True
66
+ print(f"✅ [HybridGuardian V23] Logic: Safe<{self.V2_SAFE_LIMIT} | Grey<{self.V2_GREY_LIMIT} | Panic>{self.V2_PANIC_TRIGGER}")
67
  return True
68
 
69
  except Exception as e:
70
  print(f"❌ [HybridGuardian] Init Failed: {e}")
 
71
  return False
72
 
73
  # ==========================================================================
74
  # 🧠 V2 Feature Logic (Legacy 64-Candle Sequence)
75
  # ==========================================================================
76
  def _engineer_v2_features(self, df, current_price, entry_price):
 
 
 
 
77
  try:
78
  df = df.copy()
 
79
  if len(df) < 200: return None
80
 
 
81
  df['log_ret'] = np.log(df['close'] / df['close'].shift(1))
 
82
  vol_sma = df['volume'].rolling(window=50).mean()
83
  df['rel_vol'] = (df['volume'] - vol_sma) / (vol_sma + 1e-9)
 
84
  df['rsi'] = ta.rsi(df['close'], length=14)
85
  df['rsi_norm'] = df['rsi'] / 100.0
 
86
  macd = ta.macd(df['close'], fast=12, slow=26, signal=9)
 
87
  hist_col = [c for c in macd.columns if 'MACDh' in c][0]
88
  df['macd_hist'] = macd[hist_col]
 
89
  df['roc'] = ta.roc(df['close'], length=9) / 100.0
 
90
  bb = ta.bbands(df['close'], length=20, std=2)
91
  w_col = [c for c in bb.columns if 'BBB' in c][0]
92
  p_col = [c for c in bb.columns if 'BBP' in c][0]
93
  df['bb_width'] = bb[w_col] / 100.0
94
  df['bb_pct'] = bb[p_col]
 
95
  df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
96
  df['atr_pct'] = df['atr'] / df['close']
 
97
  df['ema50'] = ta.ema(df['close'], length=50)
98
  df['ema200'] = ta.ema(df['close'], length=200)
99
  df['dist_ema50'] = (df['close'] - df['ema50']) / (df['ema50'] + 1e-9)
100
  df['dist_ema200'] = (df['close'] - df['ema200']) / (df['ema200'] + 1e-9)
 
 
101
  current_pnl = (current_price - entry_price) / entry_price
102
  df['pnl_channel'] = current_pnl
103
 
104
  df = df.fillna(0)
105
+ window = df.iloc[-64:][self.V2_FEATURES].values
106
+ if window.shape[0] < 64: return None
107
+ return window.reshape(1, -1)
108
+ except:
 
 
 
 
 
 
 
 
 
109
  return None
110
 
111
  # ==========================================================================
112
+ # 🧬 V3 Feature Logic (Advanced Multi-Timeframe)
113
  # ==========================================================================
114
  def _add_v3_indicators(self, df, window_mc):
 
115
  try:
 
116
  df['rsi'] = ta.rsi(df['close'], length=14)
117
  df['rsi_slope'] = ta.slope(df['rsi'], length=3)
 
118
  macd = ta.macd(df['close'])
119
  if macd is not None and 'MACDh_12_26_9' in macd.columns:
120
  df['macd_h'] = macd['MACDh_12_26_9']
121
  df['macd_h_slope'] = ta.slope(df['macd_h'], length=3)
122
  else:
123
+ df['macd_h'] = 0; df['macd_h_slope'] = 0
 
124
 
125
  adx = ta.adx(df['high'], df['low'], df['close'], length=14)
126
  if adx is not None and 'ADX_14' in adx.columns:
127
  df['adx'] = adx['ADX_14']
128
  df['trend_net_force'] = adx['DMP_14'] - adx['DMN_14']
129
  else:
130
+ df['adx'] = 0; df['trend_net_force'] = 0
 
131
 
 
132
  df['ema_20'] = ta.ema(df['close'], length=20)
133
  df['ema_50'] = ta.ema(df['close'], length=50)
134
  df['ema_200'] = ta.ema(df['close'], length=200)
 
135
  df['dist_ema20'] = (df['close'] - df['ema_20']) / df['close']
136
  df['dist_ema50'] = (df['close'] - df['ema_50']) / df['close']
137
  df['dist_ema200'] = (df['close'] - df['ema_200']) / df['close']
 
145
  df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
146
  df['atr_rel'] = df['atr'] / df['close']
147
 
 
148
  df['obv'] = ta.obv(df['close'], df['volume'])
149
  df['obv_slope'] = ta.slope(df['obv'], length=5)
150
  df['cmf'] = ta.cmf(df['high'], df['low'], df['close'], df['volume'], length=20)
151
 
 
152
  df['log_ret'] = np.log(df['close'] / df['close'].shift(1))
 
153
  roll_std = df['log_ret'].rolling(window=window_mc).std()
154
  roll_mean = df['log_ret'].rolling(window=window_mc).mean()
 
155
  df['mc_skew'] = df['log_ret'].rolling(window=window_mc*2).skew()
156
  df['mc_kurt'] = df['log_ret'].rolling(window=window_mc*2).kurt()
 
 
157
  drift = roll_mean - (0.5 * roll_std**2)
158
  z_score = (0 - drift) / (roll_std + 1e-9)
159
  df['mc_prob_gain'] = 1 - norm.cdf(z_score)
 
160
  var_95 = drift + (-1.645 * roll_std)
161
  df['mc_var_95'] = var_95
162
  df['mc_shock'] = (df['log_ret'] - roll_mean) / (roll_std + 1e-9)
163
 
164
  return df
165
+ except:
 
166
  return df
167
 
168
  def _engineer_v3_features(self, ohlcv_1m, ohlcv_5m, ohlcv_15m):
 
 
 
 
169
  try:
 
170
  df1 = pd.DataFrame(ohlcv_1m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
171
  df5 = pd.DataFrame(ohlcv_5m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
172
  df15 = pd.DataFrame(ohlcv_15m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
173
 
174
+ if len(df1) < 200 or len(df5) < 50 or len(df15) < 50: return None
 
 
175
 
 
176
  df1 = self._add_v3_indicators(df1, window_mc=30)
177
  df5 = self._add_v3_indicators(df5, window_mc=30)
178
  df15 = self._add_v3_indicators(df15, window_mc=20)
179
 
180
+ row1 = df1.iloc[-1]; row5 = df5.iloc[-1]; row15 = df15.iloc[-1]
 
 
 
 
 
 
181
 
182
+ combined = {}
183
+ for col in row1.index: combined[col] = row1[col]
184
+ for col in ['rsi', 'rsi_slope', 'macd_h', 'bb_pct', 'mc_prob_gain', 'mc_shock']:
185
+ if col in row5: combined[f"{col}_5m"] = row5[col]
186
+ for col in ['rsi', 'macd_h', 'trend_net_force', 'mc_prob_gain', 'dist_ema200']:
187
+ if col in row15: combined[f"{col}_15m"] = row15[col]
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  vector = []
190
  for fname in self.v3_feature_names:
191
+ val = combined.get(fname, 0.0)
192
+ if pd.isna(val) or np.isinf(val): val = 0.0
 
 
 
193
  vector.append(val)
194
 
 
195
  return np.array(vector).reshape(1, -1)
196
+ except:
 
 
 
197
  return None
198
 
199
  # ==========================================================================
200
+ # 🛡️ The Institutional Hybrid Logic (المنطق المؤسسي الجديد)
201
  # ==========================================================================
202
  def analyze_position(self, ohlcv_1m, ohlcv_5m, ohlcv_15m, entry_price):
 
 
 
203
  if not self.initialized:
204
  return {'action': 'HOLD', 'reason': 'Not Initialized'}
205
 
206
  try:
207
+ # 1. الحسابات (Inference)
208
  df_1m_raw = pd.DataFrame(ohlcv_1m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
209
  current_price = df_1m_raw['close'].iloc[-1]
210
 
211
+ # V2 Score
212
  feat_v2 = self._engineer_v2_features(df_1m_raw, current_price, entry_price)
213
+ score_v2 = float(self.model_v2.predict(xgb.DMatrix(feat_v2))[0]) if feat_v2 is not None else 0.0
 
 
 
 
 
214
 
215
+ # V3 Score
216
  feat_v3 = self._engineer_v3_features(ohlcv_1m, ohlcv_5m, ohlcv_15m)
217
+ score_v3 = float(self.model_v3.predict(xgb.DMatrix(feat_v3, feature_names=self.v3_feature_names))[0]) if feat_v3 is not None else 0.0
 
 
 
 
 
218
 
219
+ scores_dict = {'v2': score_v2, 'v3': score_v3}
220
+
221
+ # ---------------------------------------------------------
222
+ # 🧠 منطق اتخاذ القرار (Decision Logic Tree)
223
+ # ---------------------------------------------------------
224
+
225
+ # الحالة 1: V2 Panic (انهيار كارثي واضح)
226
  if score_v2 >= self.V2_PANIC_TRIGGER:
227
  return {
228
  'action': 'EXIT_HARD',
229
  'confidence': score_v2,
230
+ 'reason': f'🚨 V2 PANIC (S:{score_v2:.2f}) - Market Crash Regime',
231
+ 'scores': scores_dict
232
  }
233
 
234
+ # الحالة 2: V2 Safe Zone (سوق آمن)
235
+ elif score_v2 < self.V2_SAFE_LIMIT:
236
+ # استثناء الثقة العمياء لـ V3
237
+ if score_v3 >= self.V3_ULTRA_CONF:
238
+ return {
239
+ 'action': 'EXIT_HARD',
240
+ 'confidence': score_v3,
241
+ 'reason': f'⚡ V3 ULTRA OVERRIDE (S:{score_v3:.2f} > {self.V3_ULTRA_CONF}) - Sniper detects acute danger in Safe Zone',
242
+ 'scores': scores_dict
243
+ }
244
+ else:
245
+ return {
246
+ 'action': 'HOLD',
247
+ 'confidence': 1.0 - score_v2,
248
+ 'reason': f'✅ Safe Regime (V2:{score_v2:.2f} < {self.V2_SAFE_LIMIT}) - V3({score_v3:.2f}) Vetoed',
249
+ 'scores': scores_dict
250
+ }
251
 
252
+ # الحالة 3: V2 Grey Zone (منطقة رمادية) - نسمح لـ V3 بالتدخل بشروط
253
+ elif self.V2_SAFE_LIMIT <= score_v2 < self.V2_GREY_LIMIT:
254
+ if score_v3 >= self.V3_HARD_EXIT: # 0.90
255
+ return {
256
+ 'action': 'EXIT_HARD',
257
+ 'confidence': score_v3,
258
+ 'reason': f'🎯 V3 Sniper (S:{score_v3:.2f}) inside Grey Zone',
259
+ 'scores': scores_dict
260
+ }
261
+ elif score_v3 >= self.V3_SOFT_EXIT: # 0.75
262
+ return {
263
+ 'action': 'EXIT_SOFT',
264
+ 'confidence': score_v3,
265
+ 'reason': f'⚠️ V3 Warning (S:{score_v3:.2f}) inside Grey Zone',
266
+ 'scores': scores_dict
267
+ }
268
+ else:
269
+ return {
270
+ 'action': 'HOLD',
271
+ 'confidence': 1.0 - score_v3,
272
+ 'reason': f'👀 Grey Zone (V2:{score_v2:.2f}) - V3({score_v3:.2f}) Waiting for signal',
273
+ 'scores': scores_dict
274
+ }
275
 
276
+ # الحالة 4: V2 Risk Zone (بوابة الخطر مفتوحة) - V3 يعمل بحرية
277
+ else: # 0.60 <= score_v2 < 0.90
278
+ if score_v3 >= self.V3_HARD_EXIT:
279
+ return {
280
+ 'action': 'EXIT_HARD',
281
+ 'confidence': score_v3,
282
+ 'reason': f'🔥 V3 Kill (S:{score_v3:.2f}) [Risk Gate Open V2:{score_v2:.2f}]',
283
+ 'scores': scores_dict
284
+ }
285
+ elif score_v3 >= self.V3_SOFT_EXIT:
286
+ return {
287
+ 'action': 'EXIT_SOFT',
288
+ 'confidence': score_v3,
289
+ 'reason': f'⚠️ V3 Soft (S:{score_v3:.2f}) [Risk Gate Open V2:{score_v2:.2f}]',
290
+ 'scores': scores_dict
291
+ }
292
+ else:
293
+ return {
294
+ 'action': 'HOLD',
295
+ 'confidence': 1.0 - score_v3,
296
+ 'reason': f'🛡️ High Alert (V2:{score_v2:.2f}) but V3({score_v3:.2f}) Holds',
297
+ 'scores': scores_dict
298
+ }
299
 
300
  except Exception as e:
301
  print(f"❌ [HybridGuardian] Inference Error: {e}")