Spaces:
Paused
Paused
Update backtest_engine.py
Browse files- backtest_engine.py +36 -18
backtest_engine.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# ============================================================
|
| 2 |
-
# 🧪 backtest_engine.py (V118.
|
| 3 |
# ============================================================
|
| 4 |
|
| 5 |
import asyncio
|
|
@@ -54,7 +54,7 @@ class HeavyDutyBacktester:
|
|
| 54 |
else:
|
| 55 |
os.makedirs(CACHE_DIR)
|
| 56 |
|
| 57 |
-
print(f"🧪 [Backtest V118.
|
| 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(
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
-
|
| 303 |
-
|
| 304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 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:
|