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

Update backtest_engine.py

Browse files
Files changed (1) hide show
  1. backtest_engine.py +69 -90
backtest_engine.py CHANGED
@@ -1,5 +1,5 @@
1
  # ============================================================
2
- # 🧪 backtest_engine.py (V118.8 - GEM-Architect: Dimensionality Fix)
3
  # ============================================================
4
 
5
  import asyncio
@@ -36,19 +36,11 @@ class HeavyDutyBacktester:
36
  def __init__(self, data_manager, processor):
37
  self.dm = data_manager
38
  self.proc = processor
39
-
40
- # 🎛️ كثافة شبكة البحث
41
  self.GRID_DENSITY = 3
42
-
43
- # إعدادات المحفظة
44
  self.INITIAL_CAPITAL = 10.0
45
  self.TRADING_FEES = 0.001
46
  self.MAX_SLOTS = 4
47
-
48
- self.TARGET_COINS = [
49
- 'SOL/USDT', 'XRP/USDT', 'DOGE/USDT'
50
- ]
51
-
52
  self.force_start_date = None
53
  self.force_end_date = None
54
 
@@ -62,7 +54,7 @@ class HeavyDutyBacktester:
62
  else:
63
  os.makedirs(CACHE_DIR)
64
 
65
- print(f"🧪 [Backtest V118.8] Dimensionality Safe Mode. Models: {self._check_models_status()}")
66
 
67
  def _check_models_status(self):
68
  status = []
@@ -132,7 +124,6 @@ class HeavyDutyBacktester:
132
  df['ema50'] = ta.ema(df['close'], length=50)
133
  df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
134
 
135
- # ✅ Global Calc for rel_vol
136
  df['vol_ma50'] = df['volume'].rolling(50).mean()
137
  df['rel_vol'] = df['volume'] / (df['vol_ma50'] + 1e-9)
138
 
@@ -146,7 +137,7 @@ class HeavyDutyBacktester:
146
  df['vol_z'] = (df['volume'] - vol_mean) / (vol_std + 1e-9)
147
  df['atr_pct'] = df['atr'] / df['close']
148
 
149
- # 🔥 L1 Score 🔥
150
  rsi_penalty = np.where(df['rsi'] > 70, (df['rsi'] - 70) * 2, 0)
151
  l1_score_raw = (df['rel_vol'] * 10) + (df['atr_pct'] * 1000) - rsi_penalty
152
  df['l1_score'] = l1_score_raw.fillna(0)
@@ -197,15 +188,13 @@ class HeavyDutyBacktester:
197
  df_1m = df_1m.sort_index()
198
 
199
  frames = {}
200
- agg_dict = {'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum'}
201
-
202
  frames['1m'] = self._calculate_indicators_vectorized(df_1m.copy(), timeframe='1m')
203
  frames['1m']['timestamp'] = frames['1m'].index.floor('1min').astype(np.int64) // 10**6
204
  fast_1m = {col: frames['1m'][col].values for col in frames['1m'].columns}
205
 
206
  numpy_htf = {}
207
  for tf_str, tf_code in [('5m', '5T'), ('15m', '15T'), ('1h', '1h'), ('4h', '4h'), ('1d', '1D')]:
208
- resampled = df_1m.resample(tf_code).agg(agg_dict).dropna()
209
  resampled = self._calculate_indicators_vectorized(resampled, timeframe=tf_str)
210
  resampled['timestamp'] = resampled.index.astype(np.int64) // 10**6
211
  frames[tf_str] = resampled
@@ -227,16 +216,12 @@ class HeavyDutyBacktester:
227
  global_v2_probs = np.zeros(len(fast_1m['close']))
228
  if legacy_v2:
229
  try:
230
- l_log = fast_1m['log_ret']
231
- l_rsi = fast_1m['rsi'] / 100.0
232
- l_fib = fast_1m['fib_pos']
233
- l_vol = fast_1m['volatility']
234
-
235
  l5_log = numpy_htf['5m']['log_ret'][map_1m_to_5m]
236
  l5_rsi = numpy_htf['5m']['rsi'][map_1m_to_5m] / 100.0
237
  l5_fib = numpy_htf['5m']['fib_pos'][map_1m_to_5m]
238
  l5_trd = numpy_htf['5m']['trend_slope'][map_1m_to_5m]
239
-
240
  l15_log = numpy_htf['15m']['log_ret'][map_1m_to_15m]
241
  l15_rsi = numpy_htf['15m']['rsi'][map_1m_to_15m] / 100.0
242
  l15_fib618 = numpy_htf['15m']['dist_fib618'][map_1m_to_15m]
@@ -244,10 +229,7 @@ class HeavyDutyBacktester:
244
 
245
  lag_cols = []
246
  for lag in [1, 2, 3, 5, 10, 20]:
247
- lag_cols.extend([
248
- fast_1m[f'log_ret_lag_{lag}'], fast_1m[f'rsi_lag_{lag}'],
249
- fast_1m[f'fib_pos_lag_{lag}'], fast_1m[f'volatility_lag_{lag}']
250
- ])
251
 
252
  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])
253
  global_v2_probs = legacy_v2.predict(xgb.DMatrix(X_GLOBAL_V2))
@@ -258,13 +240,9 @@ class HeavyDutyBacktester:
258
  global_hydra_static = None
259
  if hydra_models:
260
  try:
261
- h_rsi_1m = fast_1m['rsi']
262
- h_rsi_5m = numpy_htf['5m']['rsi'][map_1m_to_5m]
263
- h_rsi_15m = numpy_htf['15m']['rsi'][map_1m_to_15m]
264
- h_bb = fast_1m['bb_width']
265
- h_vol = fast_1m['rel_vol']
266
- h_atr = fast_1m['atr']
267
- h_close = fast_1m['close']
268
  global_hydra_static = np.column_stack([h_rsi_1m, h_rsi_5m, h_rsi_15m, h_bb, h_vol, h_atr, h_close])
269
  except: pass
270
 
@@ -289,20 +267,16 @@ class HeavyDutyBacktester:
289
  idx_1h = map_1m_to_1h[final_valid_indices]
290
  idx_15m = map_1m_to_15m[final_valid_indices]
291
  idx_4h = map_1m_to_4h[final_valid_indices]
292
-
293
  titan_scores = np.clip(fast_1m['l1_score'][final_valid_indices] / 40.0, 0.1, 0.95)
294
 
295
  oracle_features = []
296
  for col in getattr(self.proc.oracle, 'feature_cols', []):
297
  if col.startswith('1h_'):
298
- c = col[3:]
299
- oracle_features.append(numpy_htf['1h'][c][idx_1h] if c in numpy_htf['1h'] else np.zeros(num_candidates))
300
  elif col.startswith('15m_'):
301
- c = col[4:]
302
- oracle_features.append(numpy_htf['15m'][c][idx_15m] if c in numpy_htf['15m'] else np.zeros(num_candidates))
303
  elif col.startswith('4h_'):
304
- c = col[3:]
305
- oracle_features.append(numpy_htf['4h'][c][idx_4h] if c in numpy_htf['4h'] else np.zeros(num_candidates))
306
  elif col == 'sim_titan_score': oracle_features.append(titan_scores)
307
  elif col == 'sim_mc_score': oracle_features.append(np.full(num_candidates, 0.5))
308
  elif col == 'sim_pattern_score': oracle_features.append(np.full(num_candidates, 0.5))
@@ -310,10 +284,8 @@ class HeavyDutyBacktester:
310
 
311
  X_oracle_big = np.column_stack(oracle_features)
312
  preds = oracle_dir_model.predict(X_oracle_big)
313
- if len(preds.shape) > 1 and preds.shape[1] > 1:
314
- oracle_preds = preds[:, 1]
315
- else:
316
- oracle_preds = preds.flatten()
317
  except Exception as e: print(f"Oracle Error: {e}")
318
 
319
  # --- B. SNIPER MATRIX CONSTRUCTION ---
@@ -327,7 +299,8 @@ class HeavyDutyBacktester:
327
  else: sniper_features.append(np.zeros(num_candidates))
328
 
329
  X_sniper_big = np.column_stack(sniper_features)
330
- preds_list = [m.predict(X_sniper_big) for m in sniper_models]
 
331
  sniper_preds = np.mean(preds_list, axis=0)
332
  except Exception as e: print(f"Sniper Error: {e}")
333
 
@@ -339,27 +312,18 @@ class HeavyDutyBacktester:
339
  chunk_size = 5000
340
  for i in range(0, num_candidates, chunk_size):
341
  chunk_indices = final_valid_indices[i : i + chunk_size]
342
- batch_X = []
343
- valid_batch_indices = []
344
 
345
  for k, idx in enumerate(chunk_indices):
346
- start = idx + 1
347
- end = start + 240
348
  sl_static = global_hydra_static[start:end]
349
-
350
  entry_p = fast_1m['close'][idx]
351
- sl_close = sl_static[:, 6]
352
- sl_atr = sl_static[:, 5]
353
-
354
  sl_dist = np.maximum(1.5 * sl_atr, entry_p * 0.015)
355
- sl_pnl = sl_close - entry_p
356
- sl_norm_pnl = sl_pnl / sl_dist
357
-
358
- sl_cum_max = np.maximum.accumulate(sl_close)
359
- sl_cum_max = np.maximum(sl_cum_max, entry_p)
360
  sl_max_pnl_r = (sl_cum_max - entry_p) / sl_dist
361
  sl_atr_pct = sl_atr / sl_close
362
-
363
  zeros = np.zeros(240); ones = np.ones(240)
364
 
365
  row = np.column_stack([
@@ -378,12 +342,10 @@ class HeavyDutyBacktester:
378
  big_X_flat = big_X.reshape(-1, big_X.shape[-1])
379
  preds_flat = hydra_models['crash'].predict_proba(big_X_flat)[:, 1]
380
  preds_batch = preds_flat.reshape(len(batch_X), 240)
381
-
382
  batch_max_risk = np.max(preds_batch, axis=1)
383
  over_thresh = preds_batch > 0.6
384
  has_crash = over_thresh.any(axis=1)
385
  crash_times_rel = np.argmax(over_thresh, axis=1)
386
-
387
  for j, glob_idx in enumerate(valid_batch_indices):
388
  hydra_risk_preds[glob_idx] = batch_max_risk[j]
389
  if has_crash[j]:
@@ -395,7 +357,6 @@ class HeavyDutyBacktester:
395
  # --- D. LEGACY V2 MAPPING ---
396
  legacy_risk_preds = np.zeros(num_candidates)
397
  legacy_time_preds = np.zeros(num_candidates, dtype=int)
398
-
399
  if legacy_v2:
400
  for k, idx in enumerate(final_valid_indices):
401
  start = idx + 1
@@ -403,31 +364,51 @@ class HeavyDutyBacktester:
403
  window = global_v2_probs[start : start + 240]
404
  legacy_risk_preds[k] = np.max(window)
405
 
406
- # --- E. FINAL DATAFRAME ---
407
- titan_scores_final = np.clip(fast_1m['l1_score'][final_valid_indices] / 40.0, 0.1, 0.95)
408
- l1_scores_final = fast_1m['l1_score'][final_valid_indices]
409
- timestamps_final = fast_1m['timestamp'][final_valid_indices]
410
- closes_final = fast_1m['close'][final_valid_indices]
411
-
412
- # FORCE FLATTEN ALL ARRAYS TO AVOID PANDAS DIMENSION ERRORS
413
- ai_df = pd.DataFrame({
414
- 'timestamp': timestamps_final.flatten(),
415
- 'symbol': sym,
416
- 'close': closes_final.flatten(),
417
- 'real_titan': titan_scores_final.flatten(),
418
- 'oracle_conf': oracle_preds.flatten(),
419
- 'sniper_score': sniper_preds.flatten(),
420
- 'l1_score': l1_scores_final.flatten(),
421
- 'risk_hydra_crash': hydra_risk_preds.flatten(),
422
- 'time_hydra_crash': hydra_time_preds.flatten(),
423
- 'risk_legacy_v2': legacy_risk_preds.flatten(),
424
- 'time_legacy_panic': legacy_time_preds.flatten()
425
- })
426
-
427
- dt = time.time() - t0
428
- if not ai_df.empty:
429
- ai_df.to_pickle(scores_file)
430
- print(f" ✅ [{sym}] Completed {len(ai_df)} signals in {dt:.2f} seconds.", flush=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
 
432
  del frames, fast_1m, numpy_htf, global_v2_probs, global_hydra_static
433
  gc.collect()
@@ -503,7 +484,6 @@ class HeavyDutyBacktester:
503
  sym_id = arr_sym_int[i]
504
  price = arr_close[i]
505
 
506
- # Exits
507
  if sym_id in positions:
508
  pos = positions[sym_id]
509
  entry = pos[0]; h_risk = pos[2]; h_time = pos[3]
@@ -521,7 +501,6 @@ class HeavyDutyBacktester:
521
  dd = (peak_bal - tot) / peak_bal
522
  if dd > max_dd: max_dd = dd
523
 
524
- # Entries
525
  if len(positions) < max_slots:
526
  if mask_buy[i]:
527
  if sym_id not in positions:
@@ -604,7 +583,7 @@ class HeavyDutyBacktester:
604
  return best['config'], best
605
 
606
  async def run_strategic_optimization_task():
607
- print("\n🧪 [STRATEGIC BACKTEST] Hyper-Vectorized Mode...")
608
  r2 = R2Service()
609
  dm = DataManager(None, None, r2)
610
  proc = MLProcessor(dm)
 
1
  # ============================================================
2
+ # 🧪 backtest_engine.py (V118.9 - GEM-Architect: Dimension Safe)
3
  # ============================================================
4
 
5
  import asyncio
 
36
  def __init__(self, data_manager, processor):
37
  self.dm = data_manager
38
  self.proc = processor
 
 
39
  self.GRID_DENSITY = 3
 
 
40
  self.INITIAL_CAPITAL = 10.0
41
  self.TRADING_FEES = 0.001
42
  self.MAX_SLOTS = 4
43
+ self.TARGET_COINS = ['SOL/USDT', 'XRP/USDT', 'DOGE/USDT']
 
 
 
 
44
  self.force_start_date = None
45
  self.force_end_date = None
46
 
 
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 = []
 
124
  df['ema50'] = ta.ema(df['close'], length=50)
125
  df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
126
 
 
127
  df['vol_ma50'] = df['volume'].rolling(50).mean()
128
  df['rel_vol'] = df['volume'] / (df['vol_ma50'] + 1e-9)
129
 
 
137
  df['vol_z'] = (df['volume'] - vol_mean) / (vol_std + 1e-9)
138
  df['atr_pct'] = df['atr'] / df['close']
139
 
140
+ # L1 Score
141
  rsi_penalty = np.where(df['rsi'] > 70, (df['rsi'] - 70) * 2, 0)
142
  l1_score_raw = (df['rel_vol'] * 10) + (df['atr_pct'] * 1000) - rsi_penalty
143
  df['l1_score'] = l1_score_raw.fillna(0)
 
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
 
216
  global_v2_probs = np.zeros(len(fast_1m['close']))
217
  if legacy_v2:
218
  try:
219
+ l_log = fast_1m['log_ret']; l_rsi = fast_1m['rsi'] / 100.0
220
+ l_fib = fast_1m['fib_pos']; l_vol = fast_1m['volatility']
 
 
 
221
  l5_log = numpy_htf['5m']['log_ret'][map_1m_to_5m]
222
  l5_rsi = numpy_htf['5m']['rsi'][map_1m_to_5m] / 100.0
223
  l5_fib = numpy_htf['5m']['fib_pos'][map_1m_to_5m]
224
  l5_trd = numpy_htf['5m']['trend_slope'][map_1m_to_5m]
 
225
  l15_log = numpy_htf['15m']['log_ret'][map_1m_to_15m]
226
  l15_rsi = numpy_htf['15m']['rsi'][map_1m_to_15m] / 100.0
227
  l15_fib618 = numpy_htf['15m']['dist_fib618'][map_1m_to_15m]
 
229
 
230
  lag_cols = []
231
  for lag in [1, 2, 3, 5, 10, 20]:
232
+ lag_cols.extend([fast_1m[f'log_ret_lag_{lag}'], fast_1m[f'rsi_lag_{lag}'], fast_1m[f'fib_pos_lag_{lag}'], fast_1m[f'volatility_lag_{lag}']])
 
 
 
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))
 
240
  global_hydra_static = None
241
  if hydra_models:
242
  try:
243
+ h_rsi_1m = fast_1m['rsi']; h_rsi_5m = numpy_htf['5m']['rsi'][map_1m_to_5m]
244
+ h_rsi_15m = numpy_htf['15m']['rsi'][map_1m_to_15m]; h_bb = fast_1m['bb_width']
245
+ h_vol = fast_1m['rel_vol']; h_atr = fast_1m['atr']; h_close = fast_1m['close']
 
 
 
 
246
  global_hydra_static = np.column_stack([h_rsi_1m, h_rsi_5m, h_rsi_15m, h_bb, h_vol, h_atr, h_close])
247
  except: pass
248
 
 
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 = []
273
  for col in getattr(self.proc.oracle, 'feature_cols', []):
274
  if col.startswith('1h_'):
275
+ c = col[3:]; oracle_features.append(numpy_htf['1h'][c][idx_1h] if c in numpy_htf['1h'] else np.zeros(num_candidates))
 
276
  elif col.startswith('15m_'):
277
+ c = col[4:]; oracle_features.append(numpy_htf['15m'][c][idx_15m] if c in numpy_htf['15m'] else np.zeros(num_candidates))
 
278
  elif col.startswith('4h_'):
279
+ c = col[3:]; oracle_features.append(numpy_htf['4h'][c][idx_4h] if c in numpy_htf['4h'] else np.zeros(num_candidates))
 
280
  elif col == 'sim_titan_score': oracle_features.append(titan_scores)
281
  elif col == 'sim_mc_score': oracle_features.append(np.full(num_candidates, 0.5))
282
  elif col == 'sim_pattern_score': oracle_features.append(np.full(num_candidates, 0.5))
 
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 ---
 
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
 
 
312
  chunk_size = 5000
313
  for i in range(0, num_candidates, chunk_size):
314
  chunk_indices = final_valid_indices[i : i + chunk_size]
315
+ batch_X = []; valid_batch_indices = []
 
316
 
317
  for k, idx in enumerate(chunk_indices):
318
+ start = idx + 1; end = start + 240
 
319
  sl_static = global_hydra_static[start:end]
 
320
  entry_p = fast_1m['close'][idx]
321
+ sl_close = sl_static[:, 6]; sl_atr = sl_static[:, 5]
 
 
322
  sl_dist = np.maximum(1.5 * sl_atr, entry_p * 0.015)
323
+ sl_pnl = sl_close - entry_p; sl_norm_pnl = sl_pnl / sl_dist
324
+ sl_cum_max = np.maximum.accumulate(sl_close); sl_cum_max = np.maximum(sl_cum_max, entry_p)
 
 
 
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([
 
342
  big_X_flat = big_X.reshape(-1, big_X.shape[-1])
343
  preds_flat = hydra_models['crash'].predict_proba(big_X_flat)[:, 1]
344
  preds_batch = preds_flat.reshape(len(batch_X), 240)
 
345
  batch_max_risk = np.max(preds_batch, axis=1)
346
  over_thresh = preds_batch > 0.6
347
  has_crash = over_thresh.any(axis=1)
348
  crash_times_rel = np.argmax(over_thresh, axis=1)
 
349
  for j, glob_idx in enumerate(valid_batch_indices):
350
  hydra_risk_preds[glob_idx] = batch_max_risk[j]
351
  if has_crash[j]:
 
357
  # --- D. LEGACY V2 MAPPING ---
358
  legacy_risk_preds = np.zeros(num_candidates)
359
  legacy_time_preds = np.zeros(num_candidates, dtype=int)
 
360
  if legacy_v2:
361
  for k, idx in enumerate(final_valid_indices):
362
  start = idx + 1
 
364
  window = global_v2_probs[start : start + 240]
365
  legacy_risk_preds[k] = np.max(window)
366
 
367
+ # --- E. FINAL DATAFRAME CONSTRUCTION (Safe Mode) ---
368
+ try:
369
+ # 1. Gather Arrays
370
+ arr_ts = fast_1m['timestamp'][final_valid_indices]
371
+ arr_close = fast_1m['close'][final_valid_indices]
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,
379
+ 'real_titan': arr_titan,
380
+ 'oracle_conf': oracle_preds,
381
+ 'sniper_score': sniper_preds,
382
+ 'l1_score': arr_l1,
383
+ 'risk_hydra_crash': hydra_risk_preds,
384
+ 'time_hydra_crash': hydra_time_preds,
385
+ 'risk_legacy_v2': legacy_risk_preds,
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
+
404
+ dt = time.time() - t0
405
+ if not ai_df.empty:
406
+ ai_df.to_pickle(scores_file)
407
+ print(f" ✅ [{sym}] Completed {len(ai_df)} signals in {dt:.2f} seconds.", flush=True)
408
+
409
+ except Exception as e:
410
+ print(f"❌ DataFrame Construction Error: {e}")
411
+ traceback.print_exc()
412
 
413
  del frames, fast_1m, numpy_htf, global_v2_probs, global_hydra_static
414
  gc.collect()
 
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
  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:
 
583
  return best['config'], best
584
 
585
  async def run_strategic_optimization_task():
586
+ print("\n🧪 [STRATEGIC BACKTEST] Dimension Safe Mode...")
587
  r2 = R2Service()
588
  dm = DataManager(None, None, r2)
589
  proc = MLProcessor(dm)