Riy777 commited on
Commit
5f64efd
·
verified ·
1 Parent(s): 992115a

Update backtest_engine.py

Browse files
Files changed (1) hide show
  1. backtest_engine.py +23 -27
backtest_engine.py CHANGED
@@ -1,9 +1,9 @@
1
  # ============================================================
2
- # 🧪 backtest_engine.py (V223.4 - GEM-Architect: Clean & Robust)
3
- # FIXES APPLIED:
4
- # 1) Added Slope & MFI calculation (Critical for Oracle & Titan).
5
- # 2) Fixed Sniper prediction shape mismatch (Broadcasting Error).
6
- # 3) Removed all citation artifacts/syntax errors.
7
  # ============================================================
8
 
9
  import asyncio
@@ -197,8 +197,7 @@ class HeavyDutyBacktester:
197
  }
198
 
199
  self.TARGET_COINS = [
200
- "SOL/USDT", "XRP/USDT", "DOGE/USDT", "ADA/USDT", "AVAX/USDT", "LINK/USDT",
201
- ]
202
 
203
  self.USE_FIXED_DATES = False
204
  self.LOOKBACK_DAYS = 14
@@ -206,7 +205,7 @@ class HeavyDutyBacktester:
206
  self.force_end_date = "2024-02-01"
207
 
208
  self.required_timeframes = self._determine_required_timeframes()
209
- print(f"🧪 [Backtest V223.4] IMMUTABLE TRUTH (Patched & Clean). TFs: {self.required_timeframes}")
210
 
211
  def _verify_system_integrity(self):
212
  errors = []
@@ -254,7 +253,7 @@ class HeavyDutyBacktester:
254
  return list(tfs)
255
 
256
  # --------------------------
257
- # Indicator Hardening Layer (FIXED)
258
  # --------------------------
259
  @staticmethod
260
  def _safe_bbands(close: pd.Series, length=20, std=2.0):
@@ -279,7 +278,7 @@ class HeavyDutyBacktester:
279
  v = df["volume"].astype(np.float64) if "volume" in df.columns else pd.Series(np.zeros(len(df)), index=df.index)
280
 
281
  # ----------------------------------------------------
282
- # ✅ [GEM-FIX] جسر التوافق مع Oracle & Titan
283
  # ----------------------------------------------------
284
 
285
  # 1. Oracle: Slope
@@ -291,24 +290,23 @@ class HeavyDutyBacktester:
291
  except: df['MFI'] = 50.0
292
 
293
  # 3. Oracle Mapping (LowerCase Aliases)
294
- # يحسب المكتبة RSI، ونحن ننسخها إلى rsi لأن النموذج يطلبها صغيرة
295
  df["RSI"] = ta.rsi(c, length=14).fillna(50)
296
- df["rsi"] = df["RSI"] # Alias for Oracle
297
 
298
  df["ATR"] = ta.atr(h, l, c, length=14).fillna(0)
299
  df["ATR_pct"] = (df["ATR"] / (c + 1e-12)) * 100
300
- df["atr_pct"] = df["ATR_pct"] # Alias for Oracle
301
 
302
  # 4. Oracle: Volume Z-Score (vol_z)
303
  vol_mean = v.rolling(20).mean()
304
  vol_std = v.rolling(20).std()
305
  df["vol_z"] = ((v - vol_mean) / (vol_std + 1e-9)).fillna(0) # For Oracle
306
 
307
- # 5. Titan: Trend Strong (Approx for lower TFs, perfect for 1d)
308
  adx_df = ta.adx(h, l, c, length=14)
309
  if adx_df is not None and not adx_df.empty:
310
  df["ADX"] = adx_df.iloc[:, 0].fillna(0)
311
- df["Trend_Strong"] = np.where(df["ADX"] > 25, 1, 0) # For Titan 1d
312
  else:
313
  df["ADX"] = 0.0
314
  df["Trend_Strong"] = 0
@@ -334,7 +332,7 @@ class HeavyDutyBacktester:
334
  for span in [9, 20, 21, 50, 200]:
335
  df[f"ema{span}"] = c.ewm(span=span, adjust=False).mean()
336
 
337
- # Derived Features
338
  df["EMA_9_dist"] = (c / (df["ema9"] + 1e-12)) - 1
339
  df["EMA_21_dist"] = (c / (df["ema21"] + 1e-12)) - 1
340
  df["EMA_50_dist"] = (c / (df["ema50"] + 1e-12)) - 1
@@ -344,6 +342,7 @@ class HeavyDutyBacktester:
344
  # BBANDS
345
  if len(df) < 30:
346
  df["lower_bb"] = c; df["upper_bb"] = c; df["bb_width"] = 0.0; df["bb_pct"] = 0.5
 
347
  else:
348
  bb = ta.bbands(c, length=20, std=2.0)
349
  if bb is not None and isinstance(bb, pd.DataFrame):
@@ -357,7 +356,6 @@ class HeavyDutyBacktester:
357
  df["lower_bb"] = bb[col_l[0]] if col_l else c
358
  df["upper_bb"] = bb[col_u[0]] if col_u else c
359
 
360
- # Aliases for Titan
361
  df["BB_w"] = df["bb_width"]
362
  df["BB_p"] = df["bb_pct"]
363
  else:
@@ -677,13 +675,9 @@ class HeavyDutyBacktester:
677
 
678
  for model in self.proc.sniper.models:
679
  raw_preds = model.predict(X_sniper)
680
-
681
- # 🛡️ Handle Multiclass Output (2D Array) vs Binary (1D Array)
682
  if len(raw_preds.shape) > 1 and raw_preds.shape[1] > 1:
683
- # Assuming index 1 is the 'Buy' probability (Standard LightGBM multiclass)
684
  preds_accum += raw_preds[:, 1].astype(np.float32)
685
  else:
686
- # Regression or Binary Logic
687
  preds_accum += raw_preds.astype(np.float32)
688
 
689
  global_sniper_scores = (preds_accum / max(1, len(self.proc.sniper.models))).astype(np.float32)
@@ -723,12 +717,14 @@ class HeavyDutyBacktester:
723
  ]
724
  ).astype(np.float32)
725
 
726
- # SAVE
727
- min_gov = float(self.GRID_RANGES["GOV_SCORE"][0])
728
- min_oracle = float(self.GRID_RANGES["ORACLE"][0])
729
- min_titan = float(self.GRID_RANGES["TITAN"][0])
730
- min_sniper = float(self.GRID_RANGES["SNIPER"][0])
731
- min_pattern = float(self.GRID_RANGES["PATTERN"][0])
 
 
732
 
733
  filter_mask = (
734
  validity_mask
 
1
  # ============================================================
2
+ # 🧪 backtest_engine.py (V223.5 - GEM-Architect: The Data Floodgates Open)
3
+ # FIXES:
4
+ # 1) Relaxed Storage Thresholds (Solves "Signals: 0").
5
+ # 2) Includes MFI/Slope/Aliases (Solves Warnings).
6
+ # 3) Includes Sniper Shape Fix (Solves Crash).
7
  # ============================================================
8
 
9
  import asyncio
 
197
  }
198
 
199
  self.TARGET_COINS = [
200
+ "SOL/USDT", "XRP/USDT", "DOGE/USDT" ]
 
201
 
202
  self.USE_FIXED_DATES = False
203
  self.LOOKBACK_DAYS = 14
 
205
  self.force_end_date = "2024-02-01"
206
 
207
  self.required_timeframes = self._determine_required_timeframes()
208
+ print(f"🧪 [Backtest V223.5] IMMUTABLE TRUTH (Patched & Open Gates). TFs: {self.required_timeframes}")
209
 
210
  def _verify_system_integrity(self):
211
  errors = []
 
253
  return list(tfs)
254
 
255
  # --------------------------
256
+ # Indicator Hardening Layer (FIXED: MFI, Slope, Aliases)
257
  # --------------------------
258
  @staticmethod
259
  def _safe_bbands(close: pd.Series, length=20, std=2.0):
 
278
  v = df["volume"].astype(np.float64) if "volume" in df.columns else pd.Series(np.zeros(len(df)), index=df.index)
279
 
280
  # ----------------------------------------------------
281
+ # ✅ [GEM-FIX] Compatibility Bridge (Oracle & Titan)
282
  # ----------------------------------------------------
283
 
284
  # 1. Oracle: Slope
 
290
  except: df['MFI'] = 50.0
291
 
292
  # 3. Oracle Mapping (LowerCase Aliases)
 
293
  df["RSI"] = ta.rsi(c, length=14).fillna(50)
294
+ df["rsi"] = df["RSI"] # Alias
295
 
296
  df["ATR"] = ta.atr(h, l, c, length=14).fillna(0)
297
  df["ATR_pct"] = (df["ATR"] / (c + 1e-12)) * 100
298
+ df["atr_pct"] = df["ATR_pct"] # Alias
299
 
300
  # 4. Oracle: Volume Z-Score (vol_z)
301
  vol_mean = v.rolling(20).mean()
302
  vol_std = v.rolling(20).std()
303
  df["vol_z"] = ((v - vol_mean) / (vol_std + 1e-9)).fillna(0) # For Oracle
304
 
305
+ # 5. Titan: Trend Strong (Approx)
306
  adx_df = ta.adx(h, l, c, length=14)
307
  if adx_df is not None and not adx_df.empty:
308
  df["ADX"] = adx_df.iloc[:, 0].fillna(0)
309
+ df["Trend_Strong"] = np.where(df["ADX"] > 25, 1, 0)
310
  else:
311
  df["ADX"] = 0.0
312
  df["Trend_Strong"] = 0
 
332
  for span in [9, 20, 21, 50, 200]:
333
  df[f"ema{span}"] = c.ewm(span=span, adjust=False).mean()
334
 
335
+ # Derived
336
  df["EMA_9_dist"] = (c / (df["ema9"] + 1e-12)) - 1
337
  df["EMA_21_dist"] = (c / (df["ema21"] + 1e-12)) - 1
338
  df["EMA_50_dist"] = (c / (df["ema50"] + 1e-12)) - 1
 
342
  # BBANDS
343
  if len(df) < 30:
344
  df["lower_bb"] = c; df["upper_bb"] = c; df["bb_width"] = 0.0; df["bb_pct"] = 0.5
345
+ df["BB_w"] = 0.0; df["BB_p"] = 0.5
346
  else:
347
  bb = ta.bbands(c, length=20, std=2.0)
348
  if bb is not None and isinstance(bb, pd.DataFrame):
 
356
  df["lower_bb"] = bb[col_l[0]] if col_l else c
357
  df["upper_bb"] = bb[col_u[0]] if col_u else c
358
 
 
359
  df["BB_w"] = df["bb_width"]
360
  df["BB_p"] = df["bb_pct"]
361
  else:
 
675
 
676
  for model in self.proc.sniper.models:
677
  raw_preds = model.predict(X_sniper)
 
 
678
  if len(raw_preds.shape) > 1 and raw_preds.shape[1] > 1:
 
679
  preds_accum += raw_preds[:, 1].astype(np.float32)
680
  else:
 
681
  preds_accum += raw_preds.astype(np.float32)
682
 
683
  global_sniper_scores = (preds_accum / max(1, len(self.proc.sniper.models))).astype(np.float32)
 
717
  ]
718
  ).astype(np.float32)
719
 
720
+ # ==========================================
721
+ # 🟢 GEM-FIX: Relaxed Saving Thresholds
722
+ # ==========================================
723
+ min_gov = 0.01
724
+ min_oracle = 0.01
725
+ min_titan = 0.01
726
+ min_sniper = 0.01
727
+ min_pattern = 0.01
728
 
729
  filter_mask = (
730
  validity_mask