Spaces:
Paused
Paused
Update backtest_engine.py
Browse files- backtest_engine.py +9 -4
backtest_engine.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
# ============================================================
|
| 2 |
-
# 🧪 backtest_engine.py (V135.
|
| 3 |
# ============================================================
|
| 4 |
|
| 5 |
import asyncio
|
|
@@ -46,7 +46,6 @@ def _z_roll(x, w=500):
|
|
| 46 |
return ((x - r) / s).fillna(0)
|
| 47 |
|
| 48 |
def _revive_score_distribution(scores):
|
| 49 |
-
"""Normalize flattened scores to 0-1 range if they are compressed"""
|
| 50 |
scores = np.array(scores, dtype=np.float32)
|
| 51 |
if len(scores) < 10: return scores
|
| 52 |
std = np.std(scores)
|
|
@@ -84,7 +83,7 @@ class HeavyDutyBacktester:
|
|
| 84 |
self.force_end_date = None
|
| 85 |
|
| 86 |
if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR)
|
| 87 |
-
print(f"🧪 [Backtest V135.
|
| 88 |
|
| 89 |
def set_date_range(self, start_str, end_str):
|
| 90 |
self.force_start_date = start_str
|
|
@@ -155,11 +154,15 @@ class HeavyDutyBacktester:
|
|
| 155 |
if adx is not None: df['ADX'] = adx.iloc[:, 0].fillna(0)
|
| 156 |
else: df['ADX'] = 0.0
|
| 157 |
|
|
|
|
| 158 |
for p in [9, 21, 50, 200]:
|
| 159 |
ema = ta.ema(df['close'], length=p)
|
| 160 |
df[f'EMA_{p}_dist'] = ((df['close'] / ema) - 1).fillna(0)
|
| 161 |
df[f'ema{p}'] = ema
|
| 162 |
|
|
|
|
|
|
|
|
|
|
| 163 |
bb = ta.bbands(df['close'], length=20, std=2.0)
|
| 164 |
if bb is not None:
|
| 165 |
df['BB_w'] = ((bb.iloc[:, 2] - bb.iloc[:, 0]) / bb.iloc[:, 1]).fillna(0)
|
|
@@ -228,7 +231,6 @@ class HeavyDutyBacktester:
|
|
| 228 |
rv_gk = ((np.log(df['high'] / df['low'])**2) / 2) - ((2 * np.log(2) - 1) * (np.log(df['close'] / df['open'])**2))
|
| 229 |
df['rv_gk'] = _z_roll(rv_gk)
|
| 230 |
|
| 231 |
-
# L_Score approximation
|
| 232 |
df['L_score'] = (df['vol_zscore_50'] - df['amihud'] - df['roll_spread'] - df['rv_gk'].abs() - df['vwap_dev'].abs() + df['ofi']).fillna(0)
|
| 233 |
|
| 234 |
# ---------------------------------------------------------
|
|
@@ -246,6 +248,8 @@ class HeavyDutyBacktester:
|
|
| 246 |
roll_min = df['low'].rolling(50).min()
|
| 247 |
diff = (roll_max - roll_min).replace(0, 1e-9)
|
| 248 |
df['fib_pos'] = ((df['close'] - roll_min) / diff).fillna(0.5)
|
|
|
|
|
|
|
| 249 |
df['trend_slope'] = ((df['ema20'] - df['ema20'].shift(5)) / df['ema20'].shift(5)).fillna(0)
|
| 250 |
df['volatility'] = (df['atr'] / df['close']).fillna(0)
|
| 251 |
|
|
@@ -481,6 +485,7 @@ class HeavyDutyBacktester:
|
|
| 481 |
l2_arr = np.full(240, 0.7)
|
| 482 |
tgt_arr = np.full(240, 3.0)
|
| 483 |
|
|
|
|
| 484 |
X_H = np.column_stack([
|
| 485 |
sl_st[:,0], sl_st[:,1], sl_st[:,2], sl_st[:,3], sl_st[:,4],
|
| 486 |
zeros, atr_pct, norm_pnl, max_pnl_r,
|
|
|
|
| 1 |
# ============================================================
|
| 2 |
+
# 🧪 backtest_engine.py (V135.1 - GEM-Architect: EMA20 Fix)
|
| 3 |
# ============================================================
|
| 4 |
|
| 5 |
import asyncio
|
|
|
|
| 46 |
return ((x - r) / s).fillna(0)
|
| 47 |
|
| 48 |
def _revive_score_distribution(scores):
|
|
|
|
| 49 |
scores = np.array(scores, dtype=np.float32)
|
| 50 |
if len(scores) < 10: return scores
|
| 51 |
std = np.std(scores)
|
|
|
|
| 83 |
self.force_end_date = None
|
| 84 |
|
| 85 |
if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR)
|
| 86 |
+
print(f"🧪 [Backtest V135.1] Feature Parity + Full Diagnostics + Speed.")
|
| 87 |
|
| 88 |
def set_date_range(self, start_str, end_str):
|
| 89 |
self.force_start_date = start_str
|
|
|
|
| 154 |
if adx is not None: df['ADX'] = adx.iloc[:, 0].fillna(0)
|
| 155 |
else: df['ADX'] = 0.0
|
| 156 |
|
| 157 |
+
# Titan uses 9, 21, 50, 200
|
| 158 |
for p in [9, 21, 50, 200]:
|
| 159 |
ema = ta.ema(df['close'], length=p)
|
| 160 |
df[f'EMA_{p}_dist'] = ((df['close'] / ema) - 1).fillna(0)
|
| 161 |
df[f'ema{p}'] = ema
|
| 162 |
|
| 163 |
+
# ✅ [GEM-FIX] Explicitly calculate EMA20 for Legacy models
|
| 164 |
+
df['ema20'] = ta.ema(df['close'], length=20).fillna(df['close'])
|
| 165 |
+
|
| 166 |
bb = ta.bbands(df['close'], length=20, std=2.0)
|
| 167 |
if bb is not None:
|
| 168 |
df['BB_w'] = ((bb.iloc[:, 2] - bb.iloc[:, 0]) / bb.iloc[:, 1]).fillna(0)
|
|
|
|
| 231 |
rv_gk = ((np.log(df['high'] / df['low'])**2) / 2) - ((2 * np.log(2) - 1) * (np.log(df['close'] / df['open'])**2))
|
| 232 |
df['rv_gk'] = _z_roll(rv_gk)
|
| 233 |
|
|
|
|
| 234 |
df['L_score'] = (df['vol_zscore_50'] - df['amihud'] - df['roll_spread'] - df['rv_gk'].abs() - df['vwap_dev'].abs() + df['ofi']).fillna(0)
|
| 235 |
|
| 236 |
# ---------------------------------------------------------
|
|
|
|
| 248 |
roll_min = df['low'].rolling(50).min()
|
| 249 |
diff = (roll_max - roll_min).replace(0, 1e-9)
|
| 250 |
df['fib_pos'] = ((df['close'] - roll_min) / diff).fillna(0.5)
|
| 251 |
+
|
| 252 |
+
# ✅ Now 'ema20' exists!
|
| 253 |
df['trend_slope'] = ((df['ema20'] - df['ema20'].shift(5)) / df['ema20'].shift(5)).fillna(0)
|
| 254 |
df['volatility'] = (df['atr'] / df['close']).fillna(0)
|
| 255 |
|
|
|
|
| 485 |
l2_arr = np.full(240, 0.7)
|
| 486 |
tgt_arr = np.full(240, 3.0)
|
| 487 |
|
| 488 |
+
# [rsi1, rsi5, rsi15, bb, vol, dist_ema, atr_p, norm, max, dists, time, entry, oracle, l2, target]
|
| 489 |
X_H = np.column_stack([
|
| 490 |
sl_st[:,0], sl_st[:,1], sl_st[:,2], sl_st[:,3], sl_st[:,4],
|
| 491 |
zeros, atr_pct, norm_pnl, max_pnl_r,
|