Riy777 commited on
Commit
481fdf5
·
verified ·
1 Parent(s): 56dcc3e

Update backtest_engine.py

Browse files
Files changed (1) hide show
  1. backtest_engine.py +94 -159
backtest_engine.py CHANGED
@@ -1,10 +1,8 @@
1
  # ============================================================
2
- # 🧪 backtest_engine.py (V43.0 - GEM-Architect: Disk-Swapped Grid)
3
  # ============================================================
4
- # التغييرات الجوهرية (Anti-Crash Architecture):
5
- # 1. التخزين المؤقت: حفظ البيانات في ملفات محلية بدلاً من RAM.
6
- # 2. قلب حلقة المعالجة: (Loop Coins -> Loop Strategies) لتقليل التحميل.
7
- # 3. استهلاك ذاكرة منخفض جداً (عملة واحدة في كل لحظة).
8
  # ============================================================
9
 
10
  import asyncio
@@ -21,16 +19,29 @@ from ml_engine.processor import MLProcessor, SystemLimits
21
  from ml_engine.data_manager import DataManager
22
  from learning_hub.adaptive_hub import StrategyDNA
23
 
24
- # كتم الضوضاء
25
  logging.getLogger('ml_engine.patterns').setLevel(logging.WARNING)
26
-
27
- # مسار الكاش المؤقت
28
  CACHE_DIR = "backtest_cache_temp"
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  class BacktestSimulator:
31
  def __init__(self, data_manager, processor):
32
  self.dm = data_manager
33
  self.proc = processor
 
34
  self.DAYS_TO_FETCH = 7
35
  self.CHUNK_LIMIT = 1000
36
 
@@ -41,27 +52,19 @@ class BacktestSimulator:
41
  'XLM/USDT', 'TRX/USDT', 'LTC/USDT'
42
  ]
43
 
44
- # إنشاء مجلد مؤقت
45
  if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR)
46
-
47
- print("🧪 [Backtest Engine V43.0] Disk-Swap Memory Protection Active.")
48
 
49
- # ==========================================================================
50
- # 1. Data Loading (Download -> Save to Disk -> Clear RAM)
51
- # ==========================================================================
52
  async def fetch_deep_history_1m(self):
53
- print(f"\n⏳ [Data] Downloading {len(self.TARGET_COINS)} coins to Disk Cache...")
54
  end_time_ms = int(time.time() * 1000)
55
  start_time_ms = end_time_ms - (self.DAYS_TO_FETCH * 24 * 60 * 60 * 1000)
56
 
57
  for sym in self.TARGET_COINS:
58
  safe_sym = sym.replace('/', '_')
59
  file_path = f"{CACHE_DIR}/{safe_sym}.pkl"
60
-
61
- # إذا كان الملف موجوداً وحديثاً، نتجاوز التحميل (تسريع)
62
  if os.path.exists(file_path):
63
- print(f" 📂 {sym:<10} [Cached]", end="", flush=True)
64
- print(f" ✅")
65
  continue
66
 
67
  print(f" ⬇️ {sym:<10}", end="", flush=True)
@@ -86,21 +89,13 @@ class BacktestSimulator:
86
  df['datetime'] = pd.to_datetime(df['timestamp'], unit='ms')
87
  df = df.set_index('datetime').sort_index()
88
 
89
- # 🔥 الحفظ على القرص وتفريغ الذاكرة
90
  df.to_pickle(file_path)
91
  print(f" ✅ Saved ({len(df)})")
92
-
93
- # تنظيف المتغيرات
94
- del df
95
- del all_candles
96
  else:
97
  print(" ⚠️ No Data")
98
-
99
- print(f"✅ Download Complete. RAM is clear.")
100
 
101
- # ==========================================================================
102
- # 2. Snapshot Helper
103
- # ==========================================================================
104
  def get_market_snapshot(self, df_full, end_idx):
105
  try:
106
  LOOKBACK_WINDOW = 6000
@@ -116,102 +111,25 @@ class BacktestSimulator:
116
  resampled = slice_1m.resample(rule).agg(agg).dropna()
117
  if len(resampled) < 20: return None
118
  timeframes[tf] = resampled[cols_order].values.tolist()
119
-
120
  return timeframes
121
  except: return None
122
 
123
- # ==========================================================================
124
- # 3. Process Logic
125
- # ==========================================================================
126
- async def process_market_layers(self, snapshot, current_price, weights, l1_threshold):
127
- # L2
128
- titan_score = 0.5
129
- if self.proc.titan:
130
- res = await asyncio.to_thread(self.proc.titan.predict, snapshot)
131
- titan_score = res.get('score', 0.5)
132
-
133
- pattern_score = 0.5
134
- if self.proc.pattern_engine:
135
- self.proc.pattern_engine.configure_thresholds(weights=SystemLimits.PATTERN_TF_WEIGHTS, bull_thresh=0.5, bear_thresh=0.4)
136
- res = await self.proc.pattern_engine.detect_chart_patterns(snapshot)
137
- pattern_score = res.get('pattern_confidence', 0.5)
138
-
139
- mc_light = 0.5
140
- if self.proc.mc_analyzer:
141
- closes = [c[4] for c in snapshot['1h']]
142
- raw = self.proc.mc_analyzer.run_light_check(closes)
143
- mc_light = 0.5 + (raw * 5.0)
144
-
145
- w_t = weights['titan']; w_p = weights['patterns']; w_m = weights['mc']
146
- total_w = w_t + w_p + w_m
147
- l2_score = ((titan_score * w_t) + (pattern_score * w_p) + (mc_light * w_m)) / total_w
148
-
149
- if l2_score < l1_threshold: return None
150
-
151
- # Oracle
152
- mc_adv_score = 0.0
153
- if self.proc.mc_analyzer:
154
- closes = [c[4] for c in snapshot['1h']]
155
- mc_adv_score = self.proc.mc_analyzer.run_advanced_simulation(closes)
156
- l3_score = l2_score + mc_adv_score
157
-
158
- oracle_decision = {'action': 'WAIT'}
159
- if self.proc.oracle:
160
- self.proc.oracle.set_threshold(0.60)
161
- oracle_input = {'ohlcv': snapshot, 'current_price': current_price, 'titan_score': titan_score, 'mc_score': mc_light, 'patterns_score': pattern_score}
162
- oracle_decision = await self.proc.oracle.predict(oracle_input)
163
-
164
- if oracle_decision['action'] not in ['BUY', 'WATCH']: return None
165
-
166
- # Sniper
167
- sniper_res = {'signal': 'WAIT'}
168
- if self.proc.sniper:
169
- self.proc.sniper.configure_settings(threshold=0.40, wall_ratio=0.9, w_ml=1.0, w_ob=0.0)
170
- sniper_res = await self.proc.sniper.check_entry_signal_async(snapshot['1m'], None)
171
-
172
- if sniper_res['signal'] == 'BUY':
173
- return {
174
- 'entry_price': sniper_res.get('entry_price', current_price),
175
- 'tp': oracle_decision.get('primary_tp'),
176
- 'sl': oracle_decision.get('sl_price'),
177
- 'oracle_conf': oracle_decision.get('confidence'),
178
- 'l2_score': l3_score
179
- }
180
- return None
181
-
182
- # ==========================================================================
183
- # 4. Inverted Simulation Loop (Fast & Low Memory)
184
- # ==========================================================================
185
  async def run_single_coin_sim(self, symbol, df_history, combinations):
186
- """
187
- تقوم بتشغيل جميع التوليفات (Combinations) على عملة واحدة دفعة واحدة.
188
- """
189
- # نتائج هذه العملة لكل توليفة
190
- # Key: Combo_Index, Value: List of Trades
191
  coin_results = {i: [] for i in range(len(combinations))}
192
-
193
  full_index = df_history.index
194
  start_idx = 6000
195
  end_idx = len(full_index) - 1
196
  current_idx = start_idx
197
 
198
- # لتقليل العمليات، نحسب المؤشرات مرة واحدة
199
- # ولكن بما أن الأوزان تغير L2 Score، يجب إعادة الحساب جزئياً
200
- # الحل: نحسب النماذج (Titan, Pattern) مرة واحدة لكل شمعة، ثم نطبق الأوزان المختلفة
201
-
202
  while current_idx < end_idx:
203
- # كل 15 دقيقة
204
- current_time = full_index[current_idx]
205
-
206
- # --- 1. حساب القيم الخام للشمعة الحالية (مرة واحدة) ---
207
  snapshot = self.get_market_snapshot(df_history, current_idx)
208
  if not snapshot:
209
- current_idx += 15
210
  continue
211
-
212
  current_price = snapshot['1m'][-1][4]
213
 
214
- # حساب قيم النماذج (ثقيلة)
215
  titan_s = 0.5
216
  if self.proc.titan: titan_s = (await asyncio.to_thread(self.proc.titan.predict, snapshot)).get('score', 0.5)
217
 
@@ -224,55 +142,67 @@ class BacktestSimulator:
224
  if self.proc.mc_analyzer:
225
  mc_s = 0.5 + (self.proc.mc_analyzer.run_light_check([c[4] for c in snapshot['1h']]) * 5.0)
226
 
227
- # --- 2. تجربة جميع التوليفات على هذه القيم الجاهزة (سريع جداً) ---
228
- for i, config in enumerate(combinations):
229
- w = config['w']
230
- l1_th = config['e_th']
231
-
232
- # حساب L2
233
- l2 = ((titan_s * w['titan']) + (patt_s * w['patterns']) + (mc_s * w['mc'])) / (w['titan']+w['patterns']+w['mc'])
234
-
235
- if l2 < l1_th: continue # فشل
236
-
237
- # Oracle & Sniper (نحسبهم مرة واحدة إذا نجح L2 لأي توليفة، لكن للتبسيط نحسبهم هنا)
238
- # بما أن Oracle/Sniper لا يعتمدون على الأوزان بشكل مباشر في قرارهم (بل يأخذونها كمدخلات)
239
- # سنقوم بتشغيلهم فقط إذا نجح L2
240
-
241
- # Oracle
242
  oracle_dec = await self.proc.oracle.predict({
243
  'ohlcv': snapshot, 'current_price': current_price,
244
  'titan_score': titan_s, 'mc_score': mc_s, 'patterns_score': patt_s
245
  })
246
- if oracle_dec['action'] not in ['BUY', 'WATCH']: continue
247
 
248
- # Sniper
 
249
  sniper_res = await self.proc.sniper.check_entry_signal_async(snapshot['1m'], None)
250
- if sniper_res['signal'] != 'BUY': continue
251
-
252
- # تسجيل نجاح صفقة لهذه التوليفة
253
- # محاكاة النتيجة (بعد ساعتين)
254
  future_idx = min(current_idx + 120, len(df_history)-1)
255
- exit_price = df_history.iloc[future_idx]['close']
256
- pnl = (exit_price - current_price) / current_price
257
-
258
- # فحص الحراس (بناءً على config)
259
- # هنا نختصر: نفترض الخروج بعد ساعتين أو TP/SL افتراضي
260
- # لتسريع الـ Grid الضخم
261
 
262
- coin_results[i].append(pnl)
263
-
264
- current_idx += 30 # قفزة 30 دقيقة
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
  return coin_results
267
 
268
- # ==========================================================================
269
- # 5. Master Grid Search (Optimized Memory)
270
- # ==========================================================================
271
  async def optimize_dna(self):
272
  best_dna = {}
273
  regimes = ['RANGE']
274
 
275
- # إعداد التوليفات
276
  weight_opts = [
277
  {'titan': 0.3, 'patterns': 0.3, 'sniper': 0.3, 'mc': 0.1},
278
  {'titan': 0.5, 'patterns': 0.2, 'sniper': 0.2, 'mc': 0.1},
@@ -284,7 +214,6 @@ class BacktestSimulator:
284
  legacy_v2_opts = [0.95, 0.98]
285
  legacy_v3_opts = [0.95]
286
 
287
- # تجميع كل الاحتمالات في قائمة واحدة
288
  combinations = []
289
  for w, e, hc, hg, l2, l3 in itertools.product(
290
  weight_opts, entry_thresh_opts, hydra_crash_opts, hydra_give_opts, legacy_v2_opts, legacy_v3_opts
@@ -294,37 +223,28 @@ class BacktestSimulator:
294
  'total_pnl_usd': 0.0, 'trades': 0, 'wins': 0
295
  })
296
 
297
- print(f"\n🧪 Testing {len(combinations)} Strategies on 20 Coins (Disk-Swapped)...")
298
 
299
- # 🔥 حلقة العملات (تحمل وتحذف واحدة تلو الأخرى)
300
  for sym in self.TARGET_COINS:
301
  safe_sym = sym.replace('/', '_')
302
  file_path = f"{CACHE_DIR}/{safe_sym}.pkl"
303
-
304
  if not os.path.exists(file_path): continue
305
 
306
- # 1. تحميل للذاكرة
307
- print(f" 👉 Processing {sym}...", end="", flush=True)
308
  df_history = pd.read_pickle(file_path)
309
 
310
- # 2. تشغيل المحاكاة السريعة
311
  results = await self.run_single_coin_sim(sym, df_history, combinations)
312
 
313
- # 3. تجميع النتائج
314
  for i, trades in results.items():
315
  for pnl in trades:
316
- # تحديث المحفظة الافتراضية لكل استراتيجية
317
- # نفترض حجم صفقة 100$
318
  profit_usd = 100.0 * pnl
319
  combinations[i]['total_pnl_usd'] += profit_usd
320
  combinations[i]['trades'] += 1
321
  if pnl > 0: combinations[i]['wins'] += 1
322
 
323
- # 4. تفريغ الذاكرة
324
  del df_history
325
  print(" Done.")
326
 
327
- # 🔥 العثور على الفائز
328
  best_combo = sorted(combinations, key=lambda x: x['total_pnl_usd'], reverse=True)[0]
329
 
330
  regime = 'RANGE'
@@ -332,6 +252,7 @@ class BacktestSimulator:
332
  "model_weights": best_combo['w'],
333
  "ob_settings": {"wall_ratio_limit": 0.4, "imbalance_thresh": 0.5},
334
  "filters": {"l1_min_score": best_combo['e_th'] * 100, "l3_conf_thresh": 0.65},
 
335
  "guard_settings": {
336
  "hydra_crash": best_combo['h_c'], "hydra_giveback": best_combo['h_g'],
337
  "legacy_v2": best_combo['l_v2'], "legacy_v3": best_combo['l_v3']
@@ -341,18 +262,18 @@ class BacktestSimulator:
341
  print("-" * 100)
342
  print(f"🏆 GRAND WINNER ({regime}):")
343
  print(f" 💰 Total Profit: ${best_combo['total_pnl_usd']:.2f}")
344
- print(f" 📊 Trades: {best_combo['trades']} (Win Rate: {(best_combo['wins']/best_combo['trades']*100 if best_combo['trades']>0 else 0):.1f}%)")
345
  print(f" ⚙️ Config: {best_combo['w']} | Thresh: {best_combo['e_th']}")
 
346
  print("=" * 100)
347
 
348
- # تنظيف الكاش
349
  try: shutil.rmtree(CACHE_DIR)
350
  except: pass
351
 
352
  return best_dna
353
 
354
  async def run_strategic_optimization_task():
355
- print("\n🧪 [STRATEGIC BACKTEST V43.0] Disk-Swapped Grid...")
356
  from r2 import R2Service
357
  r2 = R2Service()
358
  dm = DataManager(None, None, r2)
@@ -363,19 +284,33 @@ async def run_strategic_optimization_task():
363
  sim = BacktestSimulator(dm, proc)
364
  await sim.fetch_deep_history_1m()
365
 
 
366
  optimized_strategies = await sim.optimize_dna()
367
 
 
368
  from learning_hub.adaptive_hub import AdaptiveHub
369
  hub = AdaptiveHub(r2)
370
  await hub.initialize()
 
371
  for reg, data in optimized_strategies.items():
372
  if reg in hub.strategies:
 
373
  hub.strategies[reg].model_weights.update(data['model_weights'])
374
  hub.strategies[reg].filters = data['filters']
 
 
375
 
376
  await hub._save_state_to_r2()
 
 
 
 
 
 
 
 
377
  await dm.close()
378
- print("✅ [STRATEGIC BACKTEST] Completed.")
379
 
380
  if __name__ == "__main__":
381
  asyncio.run(run_strategic_optimization_task())
 
1
  # ============================================================
2
+ # 🧪 backtest_engine.py (V45.0 - GEM-Architect: Auto-Deploy)
3
  # ============================================================
4
+ # الميزة القاتلة: Find Best -> Save -> HOT RELOAD.
5
+ # بمجرد انتهاء الباكتست، النظام يتبنى الإعدادات الجديدة فوراً.
 
 
6
  # ============================================================
7
 
8
  import asyncio
 
19
  from ml_engine.data_manager import DataManager
20
  from learning_hub.adaptive_hub import StrategyDNA
21
 
 
22
  logging.getLogger('ml_engine.patterns').setLevel(logging.WARNING)
 
 
23
  CACHE_DIR = "backtest_cache_temp"
24
 
25
+ class VirtualPortfolio:
26
+ def __init__(self, initial_capital=1000.0):
27
+ self.capital = initial_capital
28
+ self.active_trades = {}
29
+ self.stats = {"max_win_usd": 0.0, "max_loss_usd": 0.0, "max_drawdown_pct": 0.0, "max_runup_pct": 0.0}
30
+ self.guardian_log = {'hydra_crash': 0, 'hydra_giveback': 0, 'legacy_v2': 0, 'legacy_v3': 0, 'tp': 0, 'sl': 0}
31
+ self.MAX_SLOTS_MAP = {'BULL': 6, 'BEAR': 3, 'RANGE': 5, 'DEAD': 2}
32
+
33
+ def can_open_trade(self, regime):
34
+ max_slots = self.MAX_SLOTS_MAP.get(regime, 4)
35
+ return len(self.active_trades) < max_slots
36
+
37
+ def calculate_size(self, confidence, regime):
38
+ return self.capital * 0.10
39
+
40
  class BacktestSimulator:
41
  def __init__(self, data_manager, processor):
42
  self.dm = data_manager
43
  self.proc = processor
44
+ self.history_cache = {}
45
  self.DAYS_TO_FETCH = 7
46
  self.CHUNK_LIMIT = 1000
47
 
 
52
  'XLM/USDT', 'TRX/USDT', 'LTC/USDT'
53
  ]
54
 
 
55
  if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR)
56
+ print("🧪 [Backtest Engine V45.0] Auto-Deploy Grid Initialized.")
 
57
 
 
 
 
58
  async def fetch_deep_history_1m(self):
59
+ print(f"\n⏳ [Data] Downloading {len(self.TARGET_COINS)} coins to Disk...")
60
  end_time_ms = int(time.time() * 1000)
61
  start_time_ms = end_time_ms - (self.DAYS_TO_FETCH * 24 * 60 * 60 * 1000)
62
 
63
  for sym in self.TARGET_COINS:
64
  safe_sym = sym.replace('/', '_')
65
  file_path = f"{CACHE_DIR}/{safe_sym}.pkl"
 
 
66
  if os.path.exists(file_path):
67
+ print(f" 📂 {sym:<10} [Cached] ")
 
68
  continue
69
 
70
  print(f" ⬇️ {sym:<10}", end="", flush=True)
 
89
  df['datetime'] = pd.to_datetime(df['timestamp'], unit='ms')
90
  df = df.set_index('datetime').sort_index()
91
 
 
92
  df.to_pickle(file_path)
93
  print(f" ✅ Saved ({len(df)})")
94
+ del df, all_candles
 
 
 
95
  else:
96
  print(" ⚠️ No Data")
97
+ print(f"✅ Download Complete.")
 
98
 
 
 
 
99
  def get_market_snapshot(self, df_full, end_idx):
100
  try:
101
  LOOKBACK_WINDOW = 6000
 
111
  resampled = slice_1m.resample(rule).agg(agg).dropna()
112
  if len(resampled) < 20: return None
113
  timeframes[tf] = resampled[cols_order].values.tolist()
 
114
  return timeframes
115
  except: return None
116
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  async def run_single_coin_sim(self, symbol, df_history, combinations):
 
 
 
 
 
118
  coin_results = {i: [] for i in range(len(combinations))}
 
119
  full_index = df_history.index
120
  start_idx = 6000
121
  end_idx = len(full_index) - 1
122
  current_idx = start_idx
123
 
 
 
 
 
124
  while current_idx < end_idx:
 
 
 
 
125
  snapshot = self.get_market_snapshot(df_history, current_idx)
126
  if not snapshot:
127
+ current_idx += 30
128
  continue
129
+
130
  current_price = snapshot['1m'][-1][4]
131
 
132
+ # حساب النماذج مرة واحدة
133
  titan_s = 0.5
134
  if self.proc.titan: titan_s = (await asyncio.to_thread(self.proc.titan.predict, snapshot)).get('score', 0.5)
135
 
 
142
  if self.proc.mc_analyzer:
143
  mc_s = 0.5 + (self.proc.mc_analyzer.run_light_check([c[4] for c in snapshot['1h']]) * 5.0)
144
 
145
+ oracle_ok = False
146
+ sniper_ok = False
147
+
148
+ oracle_dec = {'action': 'WAIT'}
149
+ if self.proc.oracle:
150
+ self.proc.oracle.set_threshold(0.50)
 
 
 
 
 
 
 
 
 
151
  oracle_dec = await self.proc.oracle.predict({
152
  'ohlcv': snapshot, 'current_price': current_price,
153
  'titan_score': titan_s, 'mc_score': mc_s, 'patterns_score': patt_s
154
  })
155
+ if oracle_dec['action'] in ['BUY', 'WATCH']: oracle_ok = True
156
 
157
+ if oracle_ok and self.proc.sniper:
158
+ self.proc.sniper.configure_settings(threshold=0.35, wall_ratio=0.9, w_ml=1.0, w_ob=0.0)
159
  sniper_res = await self.proc.sniper.check_entry_signal_async(snapshot['1m'], None)
160
+ if sniper_res['signal'] == 'BUY': sniper_ok = True
161
+
162
+ if oracle_ok and sniper_ok:
 
163
  future_idx = min(current_idx + 120, len(df_history)-1)
164
+ price_path = df_history.iloc[current_idx:future_idx]['close'].values
 
 
 
 
 
165
 
166
+ # قيم الحراس
167
+ hydra_probs = {'crash': 0.0, 'giveback': 0.0}
168
+ if self.proc.guardian_hydra:
169
+ h_res = self.proc.guardian_hydra.analyze_position(symbol, snapshot['1m'], snapshot['5m'], snapshot['15m'], {})
170
+ hydra_probs = h_res.get('probs', {'crash': 0.0, 'giveback': 0.0})
171
+
172
+ legacy_scores = {'v2': 0.0, 'v3': 0.0}
173
+ if self.proc.guardian_legacy:
174
+ l_res = self.proc.guardian_legacy.analyze_position(snapshot['1m'], snapshot['5m'], snapshot['15m'], current_price)
175
+ legacy_scores = l_res.get('scores', {'v2': 0.0, 'v3': 0.0})
176
+
177
+ for i, config in enumerate(combinations):
178
+ w = config['w']
179
+ l1_th = config['e_th']
180
+
181
+ l2 = ((titan_s * w['titan']) + (patt_s * w['patterns']) + (mc_s * w['mc'])) / (w['titan']+w['patterns']+w['mc'])
182
+ if l2 < l1_th: continue
183
+
184
+ entry_p = current_price
185
+ exit_p = price_path[-1]
186
+
187
+ if hydra_probs['crash'] >= config['h_c']:
188
+ exit_p = price_path[len(price_path)//3]
189
+ elif hydra_probs['giveback'] >= config['h_g']:
190
+ exit_p = price_path[len(price_path)//2]
191
+ elif legacy_scores['v2'] >= config['l_v2']:
192
+ exit_p = price_path[len(price_path)//3]
193
+
194
+ pnl = (exit_p - entry_p) / entry_p
195
+ coin_results[i].append(pnl)
196
+
197
+ current_idx += 30
198
 
199
  return coin_results
200
 
 
 
 
201
  async def optimize_dna(self):
202
  best_dna = {}
203
  regimes = ['RANGE']
204
 
205
+ # --- The Grand Grid ---
206
  weight_opts = [
207
  {'titan': 0.3, 'patterns': 0.3, 'sniper': 0.3, 'mc': 0.1},
208
  {'titan': 0.5, 'patterns': 0.2, 'sniper': 0.2, 'mc': 0.1},
 
214
  legacy_v2_opts = [0.95, 0.98]
215
  legacy_v3_opts = [0.95]
216
 
 
217
  combinations = []
218
  for w, e, hc, hg, l2, l3 in itertools.product(
219
  weight_opts, entry_thresh_opts, hydra_crash_opts, hydra_give_opts, legacy_v2_opts, legacy_v3_opts
 
223
  'total_pnl_usd': 0.0, 'trades': 0, 'wins': 0
224
  })
225
 
226
+ print(f"\n🧪 Testing {len(combinations)} Strategies on Disk-Swap...")
227
 
 
228
  for sym in self.TARGET_COINS:
229
  safe_sym = sym.replace('/', '_')
230
  file_path = f"{CACHE_DIR}/{safe_sym}.pkl"
 
231
  if not os.path.exists(file_path): continue
232
 
233
+ print(f" 👉 {sym}...", end="", flush=True)
 
234
  df_history = pd.read_pickle(file_path)
235
 
 
236
  results = await self.run_single_coin_sim(sym, df_history, combinations)
237
 
 
238
  for i, trades in results.items():
239
  for pnl in trades:
 
 
240
  profit_usd = 100.0 * pnl
241
  combinations[i]['total_pnl_usd'] += profit_usd
242
  combinations[i]['trades'] += 1
243
  if pnl > 0: combinations[i]['wins'] += 1
244
 
 
245
  del df_history
246
  print(" Done.")
247
 
 
248
  best_combo = sorted(combinations, key=lambda x: x['total_pnl_usd'], reverse=True)[0]
249
 
250
  regime = 'RANGE'
 
252
  "model_weights": best_combo['w'],
253
  "ob_settings": {"wall_ratio_limit": 0.4, "imbalance_thresh": 0.5},
254
  "filters": {"l1_min_score": best_combo['e_th'] * 100, "l3_conf_thresh": 0.65},
255
+ # 🔥 الحفظ الجديد في DNA
256
  "guard_settings": {
257
  "hydra_crash": best_combo['h_c'], "hydra_giveback": best_combo['h_g'],
258
  "legacy_v2": best_combo['l_v2'], "legacy_v3": best_combo['l_v3']
 
262
  print("-" * 100)
263
  print(f"🏆 GRAND WINNER ({regime}):")
264
  print(f" 💰 Total Profit: ${best_combo['total_pnl_usd']:.2f}")
265
+ print(f" 📊 Trades: {best_combo['trades']} (WR: {(best_combo['wins']/best_combo['trades']*100 if best_combo['trades']>0 else 0):.1f}%)")
266
  print(f" ⚙️ Config: {best_combo['w']} | Thresh: {best_combo['e_th']}")
267
+ print(f" 🛡️ Guards: Hydra(C:{best_combo['h_c']}/G:{best_combo['h_g']}) | Legacy(V2:{best_combo['l_v2']})")
268
  print("=" * 100)
269
 
 
270
  try: shutil.rmtree(CACHE_DIR)
271
  except: pass
272
 
273
  return best_dna
274
 
275
  async def run_strategic_optimization_task():
276
+ print("\n🧪 [STRATEGIC BACKTEST V45.0] Auto-Deploy Grid...")
277
  from r2 import R2Service
278
  r2 = R2Service()
279
  dm = DataManager(None, None, r2)
 
284
  sim = BacktestSimulator(dm, proc)
285
  await sim.fetch_deep_history_1m()
286
 
287
+ # 1. العثور على الأفضل
288
  optimized_strategies = await sim.optimize_dna()
289
 
290
+ # 2. الحفظ في R2
291
  from learning_hub.adaptive_hub import AdaptiveHub
292
  hub = AdaptiveHub(r2)
293
  await hub.initialize()
294
+
295
  for reg, data in optimized_strategies.items():
296
  if reg in hub.strategies:
297
+ # تحديث الـ DNA
298
  hub.strategies[reg].model_weights.update(data['model_weights'])
299
  hub.strategies[reg].filters = data['filters']
300
+ # إضافة guard_settings يدوياً للكائن لأننا لم نعدل الكلاس
301
+ hub.strategies[reg].guard_settings = data['guard_settings']
302
 
303
  await hub._save_state_to_r2()
304
+
305
+ # 3. 🔥 التفعيل الفوري (HOT RELOAD)
306
+ print("🚀 [Hot Reload] Applying new DNA to Live System...")
307
+ hub._inject_current_parameters()
308
+
309
+ # إذا كان هناك دالة في processor لتحديث الحراس مباشرة، نستدعيها
310
+ # بما أننا نعتمد على SystemLimits، فإن الاستدعاء القادم للحراس سيقرأ القيم الجديدة تلقائياً
311
+
312
  await dm.close()
313
+ print("✅ [STRATEGIC BACKTEST] Completed & ACTIVATED.")
314
 
315
  if __name__ == "__main__":
316
  asyncio.run(run_strategic_optimization_task())