Riy777 commited on
Commit
cda2b97
ยท
verified ยท
1 Parent(s): e6070bd

Update backtest_engine.py

Browse files
Files changed (1) hide show
  1. backtest_engine.py +289 -19
backtest_engine.py CHANGED
@@ -1,5 +1,5 @@
1
  # ============================================================
2
- # ๐Ÿงช backtest_engine.py (V82.0 - GEM-Architect: Verbose Logger)
3
  # ============================================================
4
 
5
  import asyncio
@@ -17,6 +17,7 @@ from typing import Dict, Any, List
17
  try:
18
  from ml_engine.processor import MLProcessor, SystemLimits
19
  from ml_engine.data_manager import DataManager
 
20
  from learning_hub.adaptive_hub import StrategyDNA, AdaptiveHub
21
  from r2 import R2Service
22
  except ImportError:
@@ -35,6 +36,7 @@ class HeavyDutyBacktester:
35
  self.TRADING_FEES = 0.001
36
  self.MAX_SLOTS = 4
37
 
 
38
  self.TARGET_COINS = [
39
  'SOL/USDT', 'XRP/USDT', 'DOGE/USDT', 'ADA/USDT', 'AVAX/USDT',
40
  'LINK/USDT', 'TON/USDT', 'INJ/USDT', 'APT/USDT', 'OP/USDT',
@@ -52,7 +54,7 @@ class HeavyDutyBacktester:
52
  self.force_end_date = None
53
 
54
  if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR)
55
- print(f"๐Ÿงช [Backtest V82.0] Verbose Logger Mode Ready.")
56
 
57
  def set_date_range(self, start_str, end_str):
58
  self.force_start_date = start_str
@@ -63,19 +65,18 @@ class HeavyDutyBacktester:
63
  return df[['timestamp', 'open', 'high', 'low', 'close', 'volume']].values.tolist()
64
 
65
  # ==============================================================
66
- # ๐Ÿงฑ Core Logic: Single Coin Processor (VERBOSE)
67
  # ==============================================================
68
  async def _process_single_coin_task(self, sym, start_time_ms, end_time_ms):
69
  safe_sym = sym.replace('/', '_')
70
  period_suffix = f"{start_time_ms}_{end_time_ms}"
71
  scores_file = f"{CACHE_DIR}/{safe_sym}_{period_suffix}_scores.pkl"
72
 
73
- # โœ… ุทุจุงุนุฉ ููˆุฑูŠุฉ ุฅุฐุง ุงู„ู…ู„ู ู…ูˆุฌูˆุฏ
74
  if os.path.exists(scores_file):
75
- print(f" ๐Ÿ“‚ [{sym}] Data Exists -> Skipping fetch.")
76
  return True
77
 
78
- print(f" โณ [{sym}] Starting FETCH sequence...", flush=True)
79
  t0 = time.time()
80
 
81
  all_candles_1m = []
@@ -93,38 +94,33 @@ class HeavyDutyBacktester:
93
  timeout=10.0
94
  )
95
  except:
96
- print(f" โš ๏ธ [{sym}] Net Retry...", flush=True)
97
  await asyncio.sleep(1)
98
  continue
99
 
100
- if not batch:
101
- print(f" โš ๏ธ [{sym}] No more data from exchange.", flush=True)
102
- break
103
 
104
  last_ts = batch[-1][0]
105
  if last_ts <= current_since: break
106
 
107
  all_candles_1m.extend(batch)
108
  current_since = last_ts + 1
109
-
110
  fetch_count += 1
111
- # โœ… ุทุจุงุนุฉ ุงู„ุชู‚ุฏู… ูƒู„ 5 ุฏูุนุงุช (5000 ุดู…ุนุฉ) ู„ุทู…ุฃู†ุฉ ุงู„ู…ุณุชุฎุฏู…
112
  if fetch_count % 5 == 0:
113
  print(f" -> [{sym}] Fetched {len(all_candles_1m)} candles...", flush=True)
114
 
115
  await asyncio.sleep(0.01)
116
  if current_since >= end_time_ms: break
117
 
118
- # ูู„ุชุฑุฉ ุงู„ู†ุทุงู‚ ุงู„ุฒู…ู†ูŠ ุจุฏู‚ุฉ
119
  all_candles_1m = [c for c in all_candles_1m if c[0] <= end_time_ms]
120
 
121
  if not all_candles_1m:
122
- print(f" โŒ [{sym}] FAILED: No data retrieved.")
123
  return False
124
 
125
- print(f" โœ… [{sym}] Download Complete ({len(all_candles_1m)} candles). Processing...", flush=True)
126
 
127
- # ุชุญูˆูŠู„ ุงู„ุจูŠุงู†ุงุช (Vectorization)
128
  df_1m = pd.DataFrame(all_candles_1m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
129
  cols = ['open', 'high', 'low', 'close', 'volume']
130
  df_1m[cols] = df_1m[cols].astype('float32')
@@ -147,7 +143,6 @@ class HeavyDutyBacktester:
147
  ai_results = []
148
  valid_indices = frames['5m'].index[500:]
149
 
150
- # ู…ุญุงูƒุงุฉ
151
  for t_idx in valid_indices:
152
  current_timestamp = int(t_idx.timestamp() * 1000)
153
 
@@ -203,7 +198,7 @@ class HeavyDutyBacktester:
203
  pd.DataFrame(ai_results).to_pickle(scores_file)
204
  print(f" ๐Ÿ’พ [{sym}] Saved {len(ai_results)} signals. (Time: {dt:.1f}s)")
205
  else:
206
- print(f" โš ๏ธ [{sym}] No signals found. (Time: {dt:.1f}s)")
207
 
208
  return True
209
 
@@ -223,4 +218,279 @@ class HeavyDutyBacktester:
223
  async def generate_truth_data(self):
224
  if self.force_start_date and self.force_end_date:
225
  dt_start = datetime.strptime(self.force_start_date, "%Y-%m-%d").replace(tzinfo=timezone.utc)
226
- dt_end = datetime.strptime(self.force_end_date, "%Y-%m-%d").
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # ============================================================
2
+ # ๐Ÿงช backtest_engine.py (V85.0 - GEM-Architect: Full Audit)
3
  # ============================================================
4
 
5
  import asyncio
 
17
  try:
18
  from ml_engine.processor import MLProcessor, SystemLimits
19
  from ml_engine.data_manager import DataManager
20
+ # โœ… ุงุณุชูŠุฑุงุฏ ูƒุงู…ู„ ู„ู„ู‡ุจ ูˆุงู„ุงุณุชุฑุงุชูŠุฌูŠุฉ
21
  from learning_hub.adaptive_hub import StrategyDNA, AdaptiveHub
22
  from r2 import R2Service
23
  except ImportError:
 
36
  self.TRADING_FEES = 0.001
37
  self.MAX_SLOTS = 4
38
 
39
+ # ุงู„ู‚ุงุฆู…ุฉ ุงู„ูƒุงู…ู„ุฉ (50 ุนู…ู„ุฉ)
40
  self.TARGET_COINS = [
41
  'SOL/USDT', 'XRP/USDT', 'DOGE/USDT', 'ADA/USDT', 'AVAX/USDT',
42
  'LINK/USDT', 'TON/USDT', 'INJ/USDT', 'APT/USDT', 'OP/USDT',
 
54
  self.force_end_date = None
55
 
56
  if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR)
57
+ print(f"๐Ÿงช [Backtest V85.0] Full Audit Mode (Detailed Stats).")
58
 
59
  def set_date_range(self, start_str, end_str):
60
  self.force_start_date = start_str
 
65
  return df[['timestamp', 'open', 'high', 'low', 'close', 'volume']].values.tolist()
66
 
67
  # ==============================================================
68
+ # ๐Ÿงฑ Core Logic: Single Coin Processor (Safe Scope)
69
  # ==============================================================
70
  async def _process_single_coin_task(self, sym, start_time_ms, end_time_ms):
71
  safe_sym = sym.replace('/', '_')
72
  period_suffix = f"{start_time_ms}_{end_time_ms}"
73
  scores_file = f"{CACHE_DIR}/{safe_sym}_{period_suffix}_scores.pkl"
74
 
 
75
  if os.path.exists(scores_file):
76
+ print(f" ๐Ÿ“‚ [{sym}] Data Exists -> Skipping.")
77
  return True
78
 
79
+ print(f" โš™๏ธ Simulating {sym}...", end="", flush=True)
80
  t0 = time.time()
81
 
82
  all_candles_1m = []
 
94
  timeout=10.0
95
  )
96
  except:
97
+ print(f" โš ๏ธ [{sym}] Retry...", flush=True)
98
  await asyncio.sleep(1)
99
  continue
100
 
101
+ if not batch: break
 
 
102
 
103
  last_ts = batch[-1][0]
104
  if last_ts <= current_since: break
105
 
106
  all_candles_1m.extend(batch)
107
  current_since = last_ts + 1
 
108
  fetch_count += 1
109
+
110
  if fetch_count % 5 == 0:
111
  print(f" -> [{sym}] Fetched {len(all_candles_1m)} candles...", flush=True)
112
 
113
  await asyncio.sleep(0.01)
114
  if current_since >= end_time_ms: break
115
 
 
116
  all_candles_1m = [c for c in all_candles_1m if c[0] <= end_time_ms]
117
 
118
  if not all_candles_1m:
119
+ print(f" โŒ [{sym}] No data.")
120
  return False
121
 
122
+ print(f" โœ… [{sym}] Downloaded {len(all_candles_1m)} candles. Processing...", flush=True)
123
 
 
124
  df_1m = pd.DataFrame(all_candles_1m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
125
  cols = ['open', 'high', 'low', 'close', 'volume']
126
  df_1m[cols] = df_1m[cols].astype('float32')
 
143
  ai_results = []
144
  valid_indices = frames['5m'].index[500:]
145
 
 
146
  for t_idx in valid_indices:
147
  current_timestamp = int(t_idx.timestamp() * 1000)
148
 
 
198
  pd.DataFrame(ai_results).to_pickle(scores_file)
199
  print(f" ๐Ÿ’พ [{sym}] Saved {len(ai_results)} signals. (Time: {dt:.1f}s)")
200
  else:
201
+ print(f" โš ๏ธ [{sym}] No signals. (Time: {dt:.1f}s)")
202
 
203
  return True
204
 
 
218
  async def generate_truth_data(self):
219
  if self.force_start_date and self.force_end_date:
220
  dt_start = datetime.strptime(self.force_start_date, "%Y-%m-%d").replace(tzinfo=timezone.utc)
221
+ dt_end = datetime.strptime(self.force_end_date, "%Y-%m-%d").replace(tzinfo=timezone.utc)
222
+ start_time_ms = int(dt_start.timestamp() * 1000)
223
+ end_time_ms = int(dt_end.timestamp() * 1000)
224
+ print(f"\n๐Ÿšœ [Phase 1] Processing Era: {self.force_start_date} -> {self.force_end_date}")
225
+ else:
226
+ return
227
+
228
+ chunk_size = 4
229
+ chunks = [self.TARGET_COINS[i:i + chunk_size] for i in range(0, len(self.TARGET_COINS), chunk_size)]
230
+
231
+ for chunk_idx, chunk in enumerate(chunks):
232
+ for sym in chunk:
233
+ try:
234
+ await asyncio.wait_for(
235
+ self._process_single_coin_task(sym, start_time_ms, end_time_ms),
236
+ timeout=300.0
237
+ )
238
+ except asyncio.TimeoutError:
239
+ print(f" ๐Ÿ’€ [WATCHDOG] Killed {sym}. Moving on...")
240
+ gc.collect()
241
+ gc.collect()
242
+ await asyncio.sleep(1.0)
243
+
244
+ # ==============================================================
245
+ # PHASE 2: Portfolio Digital Twin Engine (Full Audit Stats)
246
+ # ==============================================================
247
+ @staticmethod
248
+ def _worker_optimize(combinations_batch, scores_files, initial_capital, fees_pct, max_slots):
249
+ results = []
250
+ all_data = []
251
+
252
+ for fp in scores_files:
253
+ try:
254
+ df = pd.read_pickle(fp)
255
+ if not df.empty: all_data.append(df)
256
+ except: pass
257
+
258
+ if not all_data: return []
259
+
260
+ global_df = pd.concat(all_data)
261
+ global_df.sort_values('timestamp', inplace=True)
262
+ grouped_by_time = global_df.groupby('timestamp')
263
+
264
+ for config in combinations_batch:
265
+ wallet = { "balance": initial_capital, "allocated": 0.0, "positions": {}, "trades_history": [] }
266
+ w_titan = config['w_titan']
267
+ w_struct = config['w_struct']
268
+ entry_thresh = config['thresh']
269
+
270
+ # ุชุชุจุน ุงู„ู€ Drawdown
271
+ peak_balance = initial_capital
272
+ max_drawdown = 0.0
273
+
274
+ for ts, group in grouped_by_time:
275
+ active_symbols = list(wallet["positions"].keys())
276
+ current_prices = {row['symbol']: row['close'] for _, row in group.iterrows()}
277
+
278
+ # 1. Check Exits
279
+ for sym in active_symbols:
280
+ if sym in current_prices:
281
+ curr_p = current_prices[sym]
282
+ pos = wallet["positions"][sym]
283
+ entry_p = pos['entry_price']
284
+ pct_change = (curr_p - entry_p) / entry_p
285
+
286
+ if pct_change >= 0.03 or pct_change <= -0.02:
287
+ gross_pnl = pos['size_usd'] * pct_change
288
+ fees = pos['size_usd'] * fees_pct * 2
289
+ net_pnl = gross_pnl - fees
290
+ wallet["allocated"] -= pos['size_usd']
291
+ wallet["balance"] += net_pnl
292
+ del wallet["positions"][sym]
293
+ wallet["trades_history"].append({'pnl': net_pnl})
294
+
295
+ # ุชุญุฏูŠุซ ู‚ู…ุฉ ุงู„ุฑุตูŠุฏ ูˆุญุณุงุจ Drawdown
296
+ current_total_equity = wallet["balance"] # ู†ุชุฌุงู‡ู„ ุงู„ุนุงุฆู… ู„ู„ุณุฑุนุฉุŒ ู†ุญุณุจ ุงู„ู…ุญู‚ู‚ ูู‚ุท
297
+ if current_total_equity > peak_balance:
298
+ peak_balance = current_total_equity
299
+
300
+ dd = (peak_balance - current_total_equity) / peak_balance
301
+ if dd > max_drawdown: max_drawdown = dd
302
+
303
+ # 2. Check Entries
304
+ if len(wallet["positions"]) < max_slots:
305
+ free_capital = wallet["balance"] - wallet["allocated"]
306
+ slots_left = max_slots - len(wallet["positions"])
307
+
308
+ if slots_left > 0 and free_capital > 2.0:
309
+ position_size = wallet["balance"] / max_slots
310
+ if wallet["balance"] < 20.0: position_size = free_capital / slots_left
311
+ position_size = min(position_size, free_capital)
312
+
313
+ for _, row in group.iterrows():
314
+ sym = row['symbol']
315
+ if sym in wallet["positions"]: continue
316
+
317
+ sig_type = row['signal_type']
318
+ l1_raw_score = row['l1_score']
319
+ real_titan = row['real_titan']
320
+
321
+ norm_struct = 0.0
322
+ if sig_type == 'BREAKOUT': norm_struct = min(1.0, l1_raw_score / 3.0)
323
+ elif sig_type == 'REVERSAL': norm_struct = l1_raw_score / 100.0
324
+
325
+ score = 0.0
326
+ if (w_titan + w_struct) > 0:
327
+ score = ((real_titan * w_titan) + (norm_struct * w_struct)) / (w_titan + w_struct)
328
+
329
+ if score >= entry_thresh:
330
+ wallet["positions"][sym] = {'entry_price': row['close'], 'size_usd': position_size}
331
+ wallet["allocated"] += position_size
332
+ if len(wallet["positions"]) >= max_slots: break
333
+
334
+ if wallet["balance"] < 1.0 and len(wallet["positions"]) == 0: break
335
+
336
+ # ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ ุญุณุงุจ ุงู„ุฅุญุตุงุฆูŠุงุช ุงู„ุชูุตูŠู„ูŠุฉ ุงู„ูƒุงู…ู„ุฉ ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ
337
+ trades = wallet["trades_history"]
338
+ if trades:
339
+ net_profit = wallet["balance"] - initial_capital
340
+ pnls = [t['pnl'] for t in trades]
341
+ wins = [p for p in pnls if p > 0]
342
+ losses = [p for p in pnls if p <= 0]
343
+
344
+ total_trades = len(trades)
345
+ win_count = len(wins)
346
+ loss_count = len(losses)
347
+ win_rate = (win_count / total_trades) * 100 if total_trades > 0 else 0
348
+
349
+ max_single_win = max(pnls) if pnls else 0.0
350
+ max_single_loss = min(pnls) if pnls else 0.0
351
+
352
+ # ุญุณุงุจ ุงู„ุณู„ุงุณู„ ุงู„ู…ุชุชุงู„ูŠุฉ
353
+ current_win_streak = 0; max_win_streak = 0
354
+ current_loss_streak = 0; max_loss_streak = 0
355
+
356
+ for p in pnls:
357
+ if p > 0:
358
+ current_win_streak += 1
359
+ current_loss_streak = 0
360
+ if current_win_streak > max_win_streak: max_win_streak = current_win_streak
361
+ else:
362
+ current_loss_streak += 1
363
+ current_win_streak = 0
364
+ if current_loss_streak > max_loss_streak: max_loss_streak = current_loss_streak
365
+
366
+ results.append({
367
+ 'config': config,
368
+ 'final_balance': wallet["balance"],
369
+ 'net_profit': net_profit,
370
+ 'total_trades': total_trades,
371
+ 'win_count': win_count,
372
+ 'loss_count': loss_count,
373
+ 'win_rate': win_rate,
374
+ 'max_single_win': max_single_win,
375
+ 'max_single_loss': max_single_loss,
376
+ 'max_win_streak': max_win_streak,
377
+ 'max_loss_streak': max_loss_streak,
378
+ 'max_drawdown': max_drawdown * 100 # ูƒู†ุณุจุฉ ู…ุฆูˆูŠุฉ
379
+ })
380
+ else:
381
+ results.append({
382
+ 'config': config, 'final_balance': initial_capital, 'net_profit': 0.0,
383
+ 'total_trades': 0, 'win_count': 0, 'loss_count': 0, 'win_rate': 0.0,
384
+ 'max_single_win': 0.0, 'max_single_loss': 0.0, 'max_win_streak': 0,
385
+ 'max_loss_streak': 0, 'max_drawdown': 0.0
386
+ })
387
+
388
+ return results
389
+
390
+ async def run_optimization(self, target_regime="RANGE"):
391
+ await self.generate_truth_data()
392
+
393
+ start_ts = int(datetime.strptime(self.force_start_date, "%Y-%m-%d").replace(tzinfo=timezone.utc).timestamp() * 1000)
394
+ end_ts = int(datetime.strptime(self.force_end_date, "%Y-%m-%d").replace(tzinfo=timezone.utc).timestamp() * 1000)
395
+ period_id = f"{start_ts}_{end_ts}"
396
+
397
+ current_period_files = []
398
+ for f in os.listdir(CACHE_DIR):
399
+ if f.endswith('_scores.pkl') and period_id in f:
400
+ current_period_files.append(os.path.join(CACHE_DIR, f))
401
+
402
+ if not current_period_files:
403
+ print(f"โŒ No signals for {target_regime}.")
404
+ return None, None
405
+
406
+ print(f"\n๐Ÿงฉ [Phase 2] Optimizing for {target_regime}...")
407
+ print(f" ๐Ÿ’ฐ Start Capital: ${self.INITIAL_CAPITAL}")
408
+
409
+ w_titan_range = np.linspace(0.4, 0.9, num=self.GRID_DENSITY)
410
+ w_struct_range = np.linspace(0.1, 0.6, num=self.GRID_DENSITY)
411
+ thresh_range = np.linspace(0.20, 0.60, num=self.GRID_DENSITY)
412
+
413
+ combinations = []
414
+ for wt, ws, th in itertools.product(w_titan_range, w_struct_range, thresh_range):
415
+ if 0.9 <= (wt + ws) <= 1.1:
416
+ combinations.append({'w_titan': round(wt, 2), 'w_struct': round(ws, 2), 'thresh': round(th, 2)})
417
+
418
+ final_results = []
419
+ batch_size = max(20, len(combinations) // (os.cpu_count() * 2))
420
+ batches = [combinations[i:i+batch_size] for i in range(0, len(combinations), batch_size)]
421
+
422
+ with concurrent.futures.ProcessPoolExecutor() as executor:
423
+ futures = [executor.submit(self._worker_optimize, batch, current_period_files,
424
+ self.INITIAL_CAPITAL, self.TRADING_FEES, self.MAX_SLOTS)
425
+ for batch in batches]
426
+ for future in concurrent.futures.as_completed(futures):
427
+ try: final_results.extend(future.result())
428
+ except Exception as e: print(f"Grid Error: {e}")
429
+
430
+ if not final_results: return None, None
431
+
432
+ best = sorted(final_results, key=lambda x: x['final_balance'], reverse=True)[0]
433
+
434
+ # ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ The Full Audit Report ๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ
435
+ print("\n" + "="*60)
436
+ print(f"๐Ÿ† CHAMPION REPORT [{target_regime}]:")
437
+ print(f" ๐Ÿ“… Period: {self.force_start_date} -> {self.force_end_date}")
438
+ print(f" ๐Ÿ’ฐ Final Balance: ${best['final_balance']:,.2f}")
439
+ print(f" ๐Ÿš€ Net PnL: ${best['net_profit']:,.2f}")
440
+ print("-" * 60)
441
+ print(f" ๐Ÿ“Š Total Trades: {best['total_trades']}")
442
+ print(f" โœ… Winning Trades: {best['win_count']}")
443
+ print(f" โŒ Losing Trades: {best['loss_count']}")
444
+ print(f" ๐Ÿ“ˆ Win Rate: {best['win_rate']:.1f}%")
445
+ print("-" * 60)
446
+ print(f" ๐ŸŸข Max Single Win: ${best['max_single_win']:.2f}")
447
+ print(f" ๐Ÿ”ด Max Single Loss: ${best['max_single_loss']:.2f}")
448
+ print(f" ๐Ÿ”ฅ Max Win Streak: {best['max_win_streak']} trades")
449
+ print(f" ๐ŸงŠ Max Loss Streak: {best['max_loss_streak']} trades")
450
+ print(f" ๐Ÿ“‰ Max Drawdown: {best['max_drawdown']:.1f}%")
451
+ print("-" * 60)
452
+ print(f" โš™๏ธ Config: Titan={best['config']['w_titan']} | Struct={best['config']['w_struct']} | Thresh={best['config']['thresh']}")
453
+ print("="*60)
454
+
455
+ return best['config'], best
456
+
457
+ async def run_strategic_optimization_task():
458
+ print("\n๐Ÿงช [STRATEGIC BACKTEST] Time Lord Initiated...")
459
+ r2 = R2Service()
460
+ dm = DataManager(None, None, r2)
461
+ proc = MLProcessor(dm)
462
+
463
+ await dm.initialize()
464
+ await proc.initialize()
465
+
466
+ try:
467
+ hub = AdaptiveHub(r2)
468
+ await hub.initialize()
469
+
470
+ scenarios = [
471
+ {"regime": "BULL", "start": "2024-01-01", "end": "2024-03-30"},
472
+ {"regime": "BEAR", "start": "2023-08-01", "end": "2023-09-15"},
473
+ {"regime": "DEAD", "start": "2023-06-01", "end": "2023-08-01"},
474
+ {"regime": "RANGE", "start": "2024-07-01", "end": "2024-09-30"}
475
+ ]
476
+
477
+ optimizer = HeavyDutyBacktester(dm, proc)
478
+
479
+ for scen in scenarios:
480
+ target = scen["regime"]
481
+ optimizer.set_date_range(scen["start"], scen["end"])
482
+
483
+ best_config, best_stats = await optimizer.run_optimization(target_regime=target)
484
+
485
+ if best_config and best_stats:
486
+ hub.submit_challenger(target, best_config, best_stats)
487
+
488
+ await hub._save_state_to_r2()
489
+ hub._inject_current_parameters()
490
+ print(f"โœ… [System] ALL DNA Updated & Saved Successfully.")
491
+
492
+ finally:
493
+ await dm.close()
494
+
495
+ if __name__ == "__main__":
496
+ asyncio.run(run_strategic_optimization_task())