Riy777 commited on
Commit
c6ab7a4
·
verified ·
1 Parent(s): 2f08e05

Update ml_engine/hybrid_guardian.py

Browse files
Files changed (1) hide show
  1. ml_engine/hybrid_guardian.py +62 -51
ml_engine/hybrid_guardian.py CHANGED
@@ -1,5 +1,5 @@
1
  # ml_engine/hybrid_guardian.py
2
- # (V75.0 - GEM-Architect: Exact V3 Feature Match)
3
 
4
  import os
5
  import json
@@ -25,6 +25,7 @@ class HybridDeepSteward:
25
  self.model_v3 = None
26
  self.v3_feature_names = []
27
  self.initialized = False
 
28
 
29
  # Legacy V2 Features
30
  self.FEATS_1M = ['log_ret_1m', 'rsi_1m', 'fib_pos_1m', 'volatility_1m']
@@ -39,7 +40,7 @@ class HybridDeepSteward:
39
 
40
  def initialize(self):
41
  try:
42
- print(f"🕸️ [Hybrid] Initializing Precision Guardians...")
43
  if os.path.exists(self.v2_path):
44
  self.model_v2 = xgb.Booster()
45
  self.model_v2.load_model(self.v2_path)
@@ -51,7 +52,7 @@ class HybridDeepSteward:
51
  if os.path.exists(self.v3_features_path):
52
  with open(self.v3_features_path, 'r') as f:
53
  self.v3_feature_names = json.load(f)
54
- print(f" -> Loaded V3 Map: {len(self.v3_feature_names)} features.")
55
 
56
  self.initialized = True
57
  return True
@@ -75,7 +76,7 @@ class HybridDeepSteward:
75
  except: return pd.DataFrame()
76
 
77
  # ==========================================================================
78
- # 📐 V2 LEGACY FEATURE ENGINEERING
79
  # ==========================================================================
80
  def _calc_legacy_row(self, df, suffix):
81
  if len(df) < 15: return pd.Series()
@@ -130,15 +131,20 @@ class HybridDeepSteward:
130
  """Calculates Monte Carlo like stats on historical returns"""
131
  if len(returns_series) < 10: return 0, 0, 0, 0, 0
132
  r = returns_series.dropna()
133
- skew_val = skew(r)
134
- kurt_val = kurtosis(r)
135
- prob_gain = (r > 0).mean()
136
- var_95 = np.percentile(r, 5) # 5th percentile as VaR proxy
137
- shock = (r.min() - r.mean()) / (r.std() + 1e-9)
138
- return skew_val, kurt_val, prob_gain, var_95, shock
 
 
 
 
 
139
 
140
  def _enrich_dataframe(self, df):
141
- if len(df) < 50: return df
142
  c = df['close']
143
 
144
  # 1. Basic Indicators
@@ -146,53 +152,56 @@ class HybridDeepSteward:
146
  df['rsi_slope'] = self._calc_slope(df['rsi'])
147
 
148
  # 2. MACD
149
- macd = ta.macd(c)
150
- if macd is not None:
151
- df['macd_h'] = macd.iloc[:, 1].fillna(0) # Histogram
152
- df['macd_h_slope'] = self._calc_slope(df['macd_h'])
153
- else:
154
- df['macd_h'] = 0; df['macd_h_slope'] = 0
 
 
 
155
 
156
  # 3. ADX & Trend
157
- adx = ta.adx(df['high'], df['low'], c)
158
- if adx is not None:
159
- df['adx'] = adx.iloc[:, 0].fillna(0)
160
- df['dmp'] = adx.iloc[:, 1].fillna(0)
161
- df['dmn'] = adx.iloc[:, 2].fillna(0)
162
- else:
163
- df['adx'] = 0; df['dmp'] = 0; df['dmn'] = 0
 
 
 
164
 
165
- # Trend Net Force: (Close - EMA50) normalized by volatility or price
166
  ema50 = ta.ema(c, 50).fillna(c)
167
- df['trend_net_force'] = (c - ema50) / c
168
 
169
  # 4. EMAs
170
  df['ema_20'] = ta.ema(c, 20).fillna(c)
171
  df['ema_50'] = ema50
172
  df['ema_200'] = ta.ema(c, 200).fillna(c)
173
 
174
- df['dist_ema20'] = (c - df['ema_20']) / c
175
- df['dist_ema50'] = (c - df['ema_50']) / c
176
- df['dist_ema200'] = (c - df['ema_200']) / c
177
  df['slope_ema50'] = self._calc_slope(df['ema_50'], 5)
178
 
179
  # 5. Volatility & Volume
180
  df['atr'] = ta.atr(df['high'], df['low'], c, 14).fillna(0)
181
- df['atr_rel'] = df['atr'] / c
182
 
183
- df['obv'] = ta.obv(c, df['volume']).fillna(0)
184
- df['obv_slope'] = self._calc_slope(df['obv'], 5)
185
-
186
- df['cmf'] = ta.cmf(df['high'], df['low'], c, df['volume'], 20).fillna(0)
 
 
187
 
188
- # 6. Returns & MC Stats
189
  df['log_ret'] = np.log(c / c.shift(1)).fillna(0)
190
 
191
- # Rolling MC Stats (Window 50)
192
- rolling_rets = df['log_ret'].rolling(50)
193
- # Note: Rolling apply is slow, using simplified approach for performance
194
- # We calculate MC stats ONLY for the last row in _engineer_v3
195
-
196
  return df
197
 
198
  def _engineer_v3_dataframe(self, ohlcv_1m, ohlcv_5m, ohlcv_15m):
@@ -201,7 +210,7 @@ class HybridDeepSteward:
201
  df5 = self._prepare_df(ohlcv_5m)
202
  df15 = self._prepare_df(ohlcv_15m)
203
 
204
- if len(df1) < 50: return None
205
 
206
  # --- Enrich Dataframes ---
207
  df1 = self._enrich_dataframe(df1)
@@ -213,15 +222,15 @@ class HybridDeepSteward:
213
  r15 = df15.iloc[-1] if len(df15) > 0 else r1
214
 
215
  # --- MC Stats (On 1m History) ---
216
- # We calculate this on the fly for the 1m returns window
217
  mc_skew, mc_kurt, mc_prob_gain, mc_var_95, mc_shock = self._calc_mc_stats(df1['log_ret'].tail(50))
218
 
219
  # --- MC Stats (On 5m History) ---
220
- mc_prob_gain_5m = (df5['log_ret'].tail(20) > 0).mean() if len(df5) > 0 else 0
221
- mc_shock_5m = (df5['log_ret'].min() - df5['log_ret'].mean()) / df5['log_ret'].std() if len(df5) > 20 else 0
222
 
223
  # --- MC Stats (On 15m History) ---
224
- mc_prob_gain_15m = (df15['log_ret'].tail(20) > 0).mean() if len(df15) > 0 else 0
225
 
226
  # --- Construct Feature Map ---
227
  feats = {}
@@ -255,10 +264,11 @@ class HybridDeepSteward:
255
  feats['mc_prob_gain_15m'] = float(mc_prob_gain_15m)
256
  feats['dist_ema200_15m'] = float(r15.get('dist_ema200', 0.0))
257
 
258
- # 4. Numbered Placeholders ("6" to "11")
259
- # These are typically lag features or embeddings in V3.
260
- # We initialize them to 0.0 to prevent crash.
261
- for i in range(6, 12): feats[str(i)] = 0.0
 
262
 
263
  # --- Final Alignment ---
264
  df_aligned = pd.DataFrame(columns=self.v3_feature_names)
@@ -290,13 +300,13 @@ class HybridDeepSteward:
290
  if not self.initialized: return {'action': 'HOLD', 'scores': scores}
291
 
292
  try:
293
- # V2
294
  vec_v2 = self._engineer_legacy_v2_vector(ohlcv_1m, ohlcv_5m, ohlcv_15m)
295
  if vec_v2 is not None and self.model_v2:
296
  pred_v2 = self.model_v2.predict(xgb.DMatrix(vec_v2))
297
  scores['v2'] = float(pred_v2[0][2]) if len(pred_v2.shape)>1 else float(pred_v2[2]) if len(pred_v2)>1 else float(pred_v2[0])
298
 
299
- # V3 (Now with Exact Features)
300
  if self.model_v3 and self.v3_feature_names:
301
  df_v3 = self._engineer_v3_dataframe(ohlcv_1m, ohlcv_5m, ohlcv_15m)
302
  if df_v3 is not None:
@@ -320,4 +330,5 @@ class HybridDeepSteward:
320
  return {'action': 'HOLD', 'reason': f'Monitor (V2:{v2:.2f} V3:{v3:.2f})', 'scores': scores}
321
 
322
  except Exception as e:
 
323
  return {'action': 'HOLD', 'reason': 'Error', 'scores': scores}
 
1
  # ml_engine/hybrid_guardian.py
2
+ # (V80.0 - GEM-Architect: Full V3 Feature Parity)
3
 
4
  import os
5
  import json
 
25
  self.model_v3 = None
26
  self.v3_feature_names = []
27
  self.initialized = False
28
+ self.verbose = True
29
 
30
  # Legacy V2 Features
31
  self.FEATS_1M = ['log_ret_1m', 'rsi_1m', 'fib_pos_1m', 'volatility_1m']
 
40
 
41
  def initialize(self):
42
  try:
43
+ if self.verbose: print(f"🕸️ [Hybrid] Initializing Precision Guardians...")
44
  if os.path.exists(self.v2_path):
45
  self.model_v2 = xgb.Booster()
46
  self.model_v2.load_model(self.v2_path)
 
52
  if os.path.exists(self.v3_features_path):
53
  with open(self.v3_features_path, 'r') as f:
54
  self.v3_feature_names = json.load(f)
55
+ if self.verbose: print(f" -> Loaded V3 Map: {len(self.v3_feature_names)} features.")
56
 
57
  self.initialized = True
58
  return True
 
76
  except: return pd.DataFrame()
77
 
78
  # ==========================================================================
79
+ # 📐 V2 LEGACY FEATURE ENGINEERING (Keep working logic)
80
  # ==========================================================================
81
  def _calc_legacy_row(self, df, suffix):
82
  if len(df) < 15: return pd.Series()
 
131
  """Calculates Monte Carlo like stats on historical returns"""
132
  if len(returns_series) < 10: return 0, 0, 0, 0, 0
133
  r = returns_series.dropna()
134
+ if len(r) < 5: return 0, 0, 0, 0, 0
135
+
136
+ try:
137
+ skew_val = skew(r)
138
+ kurt_val = kurtosis(r)
139
+ prob_gain = (r > 0).mean()
140
+ var_95 = np.percentile(r, 5) # 5th percentile as VaR proxy
141
+ shock = (r.min() - r.mean()) / (r.std() + 1e-9)
142
+ return skew_val, kurt_val, prob_gain, var_95, shock
143
+ except:
144
+ return 0, 0, 0, 0, 0
145
 
146
  def _enrich_dataframe(self, df):
147
+ if len(df) < 30: return df
148
  c = df['close']
149
 
150
  # 1. Basic Indicators
 
152
  df['rsi_slope'] = self._calc_slope(df['rsi'])
153
 
154
  # 2. MACD
155
+ try:
156
+ macd = ta.macd(c)
157
+ if macd is not None and not macd.empty:
158
+ df['macd_h'] = macd.iloc[:, 1].fillna(0) # Histogram is usually 2nd column
159
+ df['macd_h_slope'] = self._calc_slope(df['macd_h'])
160
+ else:
161
+ df['macd_h'] = 0.0; df['macd_h_slope'] = 0.0
162
+ except:
163
+ df['macd_h'] = 0.0; df['macd_h_slope'] = 0.0
164
 
165
  # 3. ADX & Trend
166
+ try:
167
+ adx = ta.adx(df['high'], df['low'], c)
168
+ if adx is not None and not adx.empty:
169
+ df['adx'] = adx.iloc[:, 0].fillna(0)
170
+ df['dmp'] = adx.iloc[:, 1].fillna(0)
171
+ df['dmn'] = adx.iloc[:, 2].fillna(0)
172
+ else:
173
+ df['adx'] = 0.0; df['dmp'] = 0.0; df['dmn'] = 0.0
174
+ except:
175
+ df['adx'] = 0.0; df['dmp'] = 0.0; df['dmn'] = 0.0
176
 
177
+ # Trend Net Force: (Close - EMA50) normalized
178
  ema50 = ta.ema(c, 50).fillna(c)
179
+ df['trend_net_force'] = (c - ema50) / (c + 1e-9)
180
 
181
  # 4. EMAs
182
  df['ema_20'] = ta.ema(c, 20).fillna(c)
183
  df['ema_50'] = ema50
184
  df['ema_200'] = ta.ema(c, 200).fillna(c)
185
 
186
+ df['dist_ema20'] = (c - df['ema_20']) / (c + 1e-9)
187
+ df['dist_ema50'] = (c - df['ema_50']) / (c + 1e-9)
188
+ df['dist_ema200'] = (c - df['ema_200']) / (c + 1e-9)
189
  df['slope_ema50'] = self._calc_slope(df['ema_50'], 5)
190
 
191
  # 5. Volatility & Volume
192
  df['atr'] = ta.atr(df['high'], df['low'], c, 14).fillna(0)
193
+ df['atr_rel'] = df['atr'] / (c + 1e-9)
194
 
195
+ try:
196
+ df['obv'] = ta.obv(c, df['volume']).fillna(0)
197
+ df['obv_slope'] = self._calc_slope(df['obv'], 5)
198
+ df['cmf'] = ta.cmf(df['high'], df['low'], c, df['volume'], 20).fillna(0)
199
+ except:
200
+ df['obv'] = 0.0; df['obv_slope'] = 0.0; df['cmf'] = 0.0
201
 
202
+ # 6. Returns
203
  df['log_ret'] = np.log(c / c.shift(1)).fillna(0)
204
 
 
 
 
 
 
205
  return df
206
 
207
  def _engineer_v3_dataframe(self, ohlcv_1m, ohlcv_5m, ohlcv_15m):
 
210
  df5 = self._prepare_df(ohlcv_5m)
211
  df15 = self._prepare_df(ohlcv_15m)
212
 
213
+ if len(df1) < 50: return None # V3 Needs more data
214
 
215
  # --- Enrich Dataframes ---
216
  df1 = self._enrich_dataframe(df1)
 
222
  r15 = df15.iloc[-1] if len(df15) > 0 else r1
223
 
224
  # --- MC Stats (On 1m History) ---
225
+ # Calculates on the last 50 candles
226
  mc_skew, mc_kurt, mc_prob_gain, mc_var_95, mc_shock = self._calc_mc_stats(df1['log_ret'].tail(50))
227
 
228
  # --- MC Stats (On 5m History) ---
229
+ mc_prob_gain_5m = (df5['log_ret'].tail(20) > 0).mean() if len(df5) > 20 else 0
230
+ mc_shock_5m = (df5['log_ret'].min() - df5['log_ret'].mean()) / (df5['log_ret'].std()+1e-9) if len(df5) > 20 else 0
231
 
232
  # --- MC Stats (On 15m History) ---
233
+ mc_prob_gain_15m = (df15['log_ret'].tail(20) > 0).mean() if len(df15) > 20 else 0
234
 
235
  # --- Construct Feature Map ---
236
  feats = {}
 
264
  feats['mc_prob_gain_15m'] = float(mc_prob_gain_15m)
265
  feats['dist_ema200_15m'] = float(r15.get('dist_ema200', 0.0))
266
 
267
+ # 4. Numbered Placeholders (Placeholders in JSON)
268
+ # We initialize them to 0.0 to prevent crash if model uses embeddings
269
+ for i in range(0, 20):
270
+ key = str(i)
271
+ if key in self.v3_feature_names: feats[key] = 0.0
272
 
273
  # --- Final Alignment ---
274
  df_aligned = pd.DataFrame(columns=self.v3_feature_names)
 
300
  if not self.initialized: return {'action': 'HOLD', 'scores': scores}
301
 
302
  try:
303
+ # V2 (Legacy)
304
  vec_v2 = self._engineer_legacy_v2_vector(ohlcv_1m, ohlcv_5m, ohlcv_15m)
305
  if vec_v2 is not None and self.model_v2:
306
  pred_v2 = self.model_v2.predict(xgb.DMatrix(vec_v2))
307
  scores['v2'] = float(pred_v2[0][2]) if len(pred_v2.shape)>1 else float(pred_v2[2]) if len(pred_v2)>1 else float(pred_v2[0])
308
 
309
+ # V3 (Precision)
310
  if self.model_v3 and self.v3_feature_names:
311
  df_v3 = self._engineer_v3_dataframe(ohlcv_1m, ohlcv_5m, ohlcv_15m)
312
  if df_v3 is not None:
 
330
  return {'action': 'HOLD', 'reason': f'Monitor (V2:{v2:.2f} V3:{v3:.2f})', 'scores': scores}
331
 
332
  except Exception as e:
333
+ # traceback.print_exc()
334
  return {'action': 'HOLD', 'reason': 'Error', 'scores': scores}