Riy777 commited on
Commit
d01877f
·
verified ·
1 Parent(s): 67a0513

Update backtest_engine.py

Browse files
Files changed (1) hide show
  1. backtest_engine.py +36 -18
backtest_engine.py CHANGED
@@ -1,5 +1,5 @@
1
  # ============================================================
2
- # 🧪 backtest_engine.py (V118.9 - GEM-Architect: Dimension Safe)
3
  # ============================================================
4
 
5
  import asyncio
@@ -54,7 +54,7 @@ class HeavyDutyBacktester:
54
  else:
55
  os.makedirs(CACHE_DIR)
56
 
57
- print(f"🧪 [Backtest V118.9] Dimension Safe Mode. Models: {self._check_models_status()}")
58
 
59
  def _check_models_status(self):
60
  status = []
@@ -188,13 +188,15 @@ class HeavyDutyBacktester:
188
  df_1m = df_1m.sort_index()
189
 
190
  frames = {}
 
 
191
  frames['1m'] = self._calculate_indicators_vectorized(df_1m.copy(), timeframe='1m')
192
  frames['1m']['timestamp'] = frames['1m'].index.floor('1min').astype(np.int64) // 10**6
193
  fast_1m = {col: frames['1m'][col].values for col in frames['1m'].columns}
194
 
195
  numpy_htf = {}
196
  for tf_str, tf_code in [('5m', '5T'), ('15m', '15T'), ('1h', '1h'), ('4h', '4h'), ('1d', '1D')]:
197
- resampled = df_1m.resample(tf_code).agg({'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum'}).dropna()
198
  resampled = self._calculate_indicators_vectorized(resampled, timeframe=tf_str)
199
  resampled['timestamp'] = resampled.index.astype(np.int64) // 10**6
200
  frames[tf_str] = resampled
@@ -233,7 +235,11 @@ class HeavyDutyBacktester:
233
 
234
  X_GLOBAL_V2 = np.column_stack([l_log, l_rsi, l_fib, l_vol, l5_log, l5_rsi, l5_fib, l5_trd, l15_log, l15_rsi, l15_fib618, l15_trd, *lag_cols])
235
  global_v2_probs = legacy_v2.predict(xgb.DMatrix(X_GLOBAL_V2))
236
- if len(global_v2_probs.shape) > 1: global_v2_probs = global_v2_probs[:, 2]
 
 
 
 
237
  except: pass
238
 
239
  # 5. 🔥 Pre-Assemble Hydra Static 🔥
@@ -267,6 +273,7 @@ class HeavyDutyBacktester:
267
  idx_1h = map_1m_to_1h[final_valid_indices]
268
  idx_15m = map_1m_to_15m[final_valid_indices]
269
  idx_4h = map_1m_to_4h[final_valid_indices]
 
270
  titan_scores = np.clip(fast_1m['l1_score'][final_valid_indices] / 40.0, 0.1, 0.95)
271
 
272
  oracle_features = []
@@ -284,11 +291,13 @@ class HeavyDutyBacktester:
284
 
285
  X_oracle_big = np.column_stack(oracle_features)
286
  preds = oracle_dir_model.predict(X_oracle_big)
287
- if len(preds.shape) > 1 and preds.shape[1] > 1: oracle_preds = preds[:, 1]
 
 
288
  else: oracle_preds = preds.flatten()
289
  except Exception as e: print(f"Oracle Error: {e}")
290
 
291
- # --- B. SNIPER MATRIX CONSTRUCTION ---
292
  sniper_preds = np.full(num_candidates, 0.5)
293
  if sniper_models:
294
  try:
@@ -299,9 +308,19 @@ class HeavyDutyBacktester:
299
  else: sniper_features.append(np.zeros(num_candidates))
300
 
301
  X_sniper_big = np.column_stack(sniper_features)
302
- # ✅ FIX: SQUEEZE PREDICTIONS
303
- preds_list = [np.squeeze(m.predict(X_sniper_big)) for m in sniper_models]
304
- sniper_preds = np.mean(preds_list, axis=0)
 
 
 
 
 
 
 
 
 
 
305
  except Exception as e: print(f"Sniper Error: {e}")
306
 
307
  # --- C. HYDRA MATRIX CONSTRUCTION ---
@@ -325,7 +344,6 @@ class HeavyDutyBacktester:
325
  sl_max_pnl_r = (sl_cum_max - entry_p) / sl_dist
326
  sl_atr_pct = sl_atr / sl_close
327
  zeros = np.zeros(240); ones = np.ones(240)
328
-
329
  row = np.column_stack([
330
  sl_static[:, 0], sl_static[:, 1], sl_static[:, 2],
331
  sl_static[:, 3], sl_static[:, 4],
@@ -372,7 +390,7 @@ class HeavyDutyBacktester:
372
  arr_l1 = fast_1m['l1_score'][final_valid_indices]
373
  arr_titan = np.clip(arr_l1 / 40.0, 0.1, 0.95)
374
 
375
- # 2. Check Lengths
376
  arrays = {
377
  'timestamp': arr_ts,
378
  'close': arr_close,
@@ -386,18 +404,16 @@ class HeavyDutyBacktester:
386
  'time_legacy_panic': legacy_time_preds
387
  }
388
 
389
- # 3. Explicitly Flatten & Verify
390
  clean_arrays = {}
391
  for k, v in arrays.items():
392
  flat_v = np.array(v).flatten()
393
- if len(flat_v) != num_candidates:
394
- print(f"❌ SIZE MISMATCH in {k}: Expected {num_candidates}, got {len(flat_v)}")
395
- # Fix by truncating or padding (Emergency Fix)
396
- if len(flat_v) > num_candidates: flat_v = flat_v[:num_candidates]
397
- else: flat_v = np.pad(flat_v, (0, num_candidates - len(flat_v)))
398
  clean_arrays[k] = flat_v
399
 
400
- # 4. Create DF
401
  clean_arrays['symbol'] = sym
402
  ai_df = pd.DataFrame(clean_arrays)
403
 
@@ -484,6 +500,7 @@ class HeavyDutyBacktester:
484
  sym_id = arr_sym_int[i]
485
  price = arr_close[i]
486
 
 
487
  if sym_id in positions:
488
  pos = positions[sym_id]
489
  entry = pos[0]; h_risk = pos[2]; h_time = pos[3]
@@ -501,6 +518,7 @@ class HeavyDutyBacktester:
501
  dd = (peak_bal - tot) / peak_bal
502
  if dd > max_dd: max_dd = dd
503
 
 
504
  if len(positions) < max_slots:
505
  if mask_buy[i]:
506
  if sym_id not in positions:
 
1
  # ============================================================
2
+ # 🧪 backtest_engine.py (V118.10 - GEM-Architect: Multi-Class Fix)
3
  # ============================================================
4
 
5
  import asyncio
 
54
  else:
55
  os.makedirs(CACHE_DIR)
56
 
57
+ print(f"🧪 [Backtest V118.10] Multi-Class Fix Mode. Models: {self._check_models_status()}")
58
 
59
  def _check_models_status(self):
60
  status = []
 
188
  df_1m = df_1m.sort_index()
189
 
190
  frames = {}
191
+ agg_dict = {'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum'}
192
+
193
  frames['1m'] = self._calculate_indicators_vectorized(df_1m.copy(), timeframe='1m')
194
  frames['1m']['timestamp'] = frames['1m'].index.floor('1min').astype(np.int64) // 10**6
195
  fast_1m = {col: frames['1m'][col].values for col in frames['1m'].columns}
196
 
197
  numpy_htf = {}
198
  for tf_str, tf_code in [('5m', '5T'), ('15m', '15T'), ('1h', '1h'), ('4h', '4h'), ('1d', '1D')]:
199
+ resampled = df_1m.resample(tf_code).agg(agg_dict).dropna()
200
  resampled = self._calculate_indicators_vectorized(resampled, timeframe=tf_str)
201
  resampled['timestamp'] = resampled.index.astype(np.int64) // 10**6
202
  frames[tf_str] = resampled
 
235
 
236
  X_GLOBAL_V2 = np.column_stack([l_log, l_rsi, l_fib, l_vol, l5_log, l5_rsi, l5_fib, l5_trd, l15_log, l15_rsi, l15_fib618, l15_trd, *lag_cols])
237
  global_v2_probs = legacy_v2.predict(xgb.DMatrix(X_GLOBAL_V2))
238
+
239
+ # ✅ FIX: Handle Multi-Class output if exists
240
+ if len(global_v2_probs.shape) > 1:
241
+ # Assuming last column is Panic/Crash prob (or index 2)
242
+ global_v2_probs = global_v2_probs[:, -1]
243
  except: pass
244
 
245
  # 5. 🔥 Pre-Assemble Hydra Static 🔥
 
273
  idx_1h = map_1m_to_1h[final_valid_indices]
274
  idx_15m = map_1m_to_15m[final_valid_indices]
275
  idx_4h = map_1m_to_4h[final_valid_indices]
276
+
277
  titan_scores = np.clip(fast_1m['l1_score'][final_valid_indices] / 40.0, 0.1, 0.95)
278
 
279
  oracle_features = []
 
291
 
292
  X_oracle_big = np.column_stack(oracle_features)
293
  preds = oracle_dir_model.predict(X_oracle_big)
294
+
295
+ # ✅ FIX: Handle Multi-Class (take last column usually)
296
+ if len(preds.shape) > 1 and preds.shape[1] > 1: oracle_preds = preds[:, -1]
297
  else: oracle_preds = preds.flatten()
298
  except Exception as e: print(f"Oracle Error: {e}")
299
 
300
+ # --- B. SNIPER MATRIX CONSTRUCTION (FIXED) ---
301
  sniper_preds = np.full(num_candidates, 0.5)
302
  if sniper_models:
303
  try:
 
308
  else: sniper_features.append(np.zeros(num_candidates))
309
 
310
  X_sniper_big = np.column_stack(sniper_features)
311
+
312
+ # FIX: Extract Positive Class Prob if Multi-Class
313
+ batch_preds = []
314
+ for m in sniper_models:
315
+ raw_p = m.predict(X_sniper_big)
316
+ if len(raw_p.shape) > 1 and raw_p.shape[1] > 1:
317
+ # Assuming index 2 is Buy (0=Sell, 1=Hold, 2=Buy) or index 1 if binary
318
+ # Safest: Take last column
319
+ batch_preds.append(raw_p[:, -1])
320
+ else:
321
+ batch_preds.append(raw_p)
322
+
323
+ sniper_preds = np.mean(batch_preds, axis=0)
324
  except Exception as e: print(f"Sniper Error: {e}")
325
 
326
  # --- C. HYDRA MATRIX CONSTRUCTION ---
 
344
  sl_max_pnl_r = (sl_cum_max - entry_p) / sl_dist
345
  sl_atr_pct = sl_atr / sl_close
346
  zeros = np.zeros(240); ones = np.ones(240)
 
347
  row = np.column_stack([
348
  sl_static[:, 0], sl_static[:, 1], sl_static[:, 2],
349
  sl_static[:, 3], sl_static[:, 4],
 
390
  arr_l1 = fast_1m['l1_score'][final_valid_indices]
391
  arr_titan = np.clip(arr_l1 / 40.0, 0.1, 0.95)
392
 
393
+ # 2. Check Lengths & Flatten
394
  arrays = {
395
  'timestamp': arr_ts,
396
  'close': arr_close,
 
404
  'time_legacy_panic': legacy_time_preds
405
  }
406
 
 
407
  clean_arrays = {}
408
  for k, v in arrays.items():
409
  flat_v = np.array(v).flatten()
410
+ # Safety Truncate
411
+ if len(flat_v) > num_candidates: flat_v = flat_v[:num_candidates]
412
+ elif len(flat_v) < num_candidates:
413
+ print(f"⚠️ PADDING {k}: {len(flat_v)} -> {num_candidates}")
414
+ flat_v = np.pad(flat_v, (0, num_candidates - len(flat_v)))
415
  clean_arrays[k] = flat_v
416
 
 
417
  clean_arrays['symbol'] = sym
418
  ai_df = pd.DataFrame(clean_arrays)
419
 
 
500
  sym_id = arr_sym_int[i]
501
  price = arr_close[i]
502
 
503
+ # Exits
504
  if sym_id in positions:
505
  pos = positions[sym_id]
506
  entry = pos[0]; h_risk = pos[2]; h_time = pos[3]
 
518
  dd = (peak_bal - tot) / peak_bal
519
  if dd > max_dd: max_dd = dd
520
 
521
+ # Entries
522
  if len(positions) < max_slots:
523
  if mask_buy[i]:
524
  if sym_id not in positions: