Riy777 commited on
Commit
0905f47
·
verified ·
1 Parent(s): e3af292

Update backtest_engine.py

Browse files
Files changed (1) hide show
  1. backtest_engine.py +118 -102
backtest_engine.py CHANGED
@@ -1,5 +1,5 @@
1
  # ============================================================
2
- # 🧪 backtest_engine.py (V71.1 - GEM-Architect: 6-Frame Turbo)
3
  # ============================================================
4
 
5
  import asyncio
@@ -9,6 +9,7 @@ import time
9
  import logging
10
  import itertools
11
  import os
 
12
  import concurrent.futures
13
  from typing import Dict, Any, List
14
 
@@ -43,7 +44,7 @@ class HeavyDutyBacktester:
43
  ]
44
 
45
  if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR)
46
- print(f"🧪 [Backtest V71.1] 6-Frame Turbo Mode (1m Included).")
47
 
48
  # ==============================================================
49
  # 🛠️ Helpers
@@ -53,7 +54,7 @@ class HeavyDutyBacktester:
53
  return df[['timestamp', 'open', 'high', 'low', 'close', 'volume']].values.tolist()
54
 
55
  # ==============================================================
56
- # PHASE 1: Generate Truth Data (Multi-Frame Vectorization)
57
  # ==============================================================
58
  async def generate_truth_data(self):
59
  print(f"\n🚜 [Phase 1] Processing Logic Tree + Titan ({self.BACKTEST_DAYS} Days)...")
@@ -64,127 +65,142 @@ class HeavyDutyBacktester:
64
  safe_sym = sym.replace('/', '_')
65
  scores_file = f"{CACHE_DIR}/{safe_sym}_fullstack_scores.pkl"
66
 
 
 
 
67
  if os.path.exists(scores_file):
68
  print(f" 📂 {sym} scores ready. Skipping.")
69
  continue
70
 
71
  print(f" ⚙️ Simulating {sym}...", end="", flush=True)
72
 
73
- # 1. جلب بيانات الدقيقة الخام
74
  all_candles_1m = []
75
- current_since = start_time_ms
76
- while current_since < end_time_ms:
77
- try:
78
- batch = await self.dm.exchange.fetch_ohlcv(sym, '1m', since=current_since, limit=1000)
79
- if not batch: break
80
- last_ts = batch[-1][0]
81
- if last_ts <= current_since: break
82
- all_candles_1m.extend(batch)
83
- current_since = last_ts + 1
84
- await asyncio.sleep(0.01)
85
- if current_since >= end_time_ms: break
86
- except: await asyncio.sleep(0.5)
87
-
88
- all_candles_1m = [c for c in all_candles_1m if c[0] <= end_time_ms]
89
- if not all_candles_1m:
90
- print(" No Data.")
91
- continue
92
-
93
- df_1m = pd.DataFrame(all_candles_1m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
94
- df_1m['datetime'] = pd.to_datetime(df_1m['timestamp'], unit='ms')
95
- df_1m.set_index('datetime', inplace=True)
96
- df_1m = df_1m.sort_index()
97
-
98
- # 🔥🔥🔥 التحضير المسبق (Vectorization) 🔥🔥🔥
99
- agg_dict = {'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum'}
100
-
101
  frames = {}
102
 
103
- # ✅ إضافة إطار الدقيقة (1m) مباشرة
104
- # لا نحتاج Resample لأنه هو الأساس، فقط نضيف عمود timestamp
105
- df_1m_ready = df_1m.copy()
106
- df_1m_ready['timestamp'] = df_1m_ready.index.astype(np.int64) // 10**6
107
- frames['1m'] = df_1m_ready
108
-
109
- # إضافة باقي الأطر (Resampling)
110
- for tf_str, tf_code in [('5m', '5T'), ('15m', '15T'), ('1h', '1h'), ('4h', '4h'), ('1d', '1D')]:
111
- resampled = df_1m.resample(tf_code).agg(agg_dict).dropna()
112
- resampled['timestamp'] = resampled.index.astype(np.int64) // 10**6
113
- frames[tf_str] = resampled
 
 
 
 
 
 
 
 
 
114
 
115
- ai_results = []
116
-
117
- # نتحرك بناءً على إطار الـ 5 دقائق كقاعدة زمنية
118
- valid_indices = frames['5m'].index[500:]
119
-
120
- for t_idx in valid_indices:
121
- current_timestamp = int(t_idx.timestamp() * 1000)
122
 
123
- # 🔥 قص البيانات لجميع الأطر الستة (1m included)
124
- ohlcv_data = {}
125
 
126
- try:
127
- # [FIXED] إضافة 1m للحزمة (نأخذ 500 شمعة للدقة العالية)
128
- ohlcv_data['1m'] = self.df_to_list(frames['1m'].loc[:t_idx].tail(500))
129
-
130
- ohlcv_data['5m'] = self.df_to_list(frames['5m'].loc[:t_idx].tail(200))
131
- ohlcv_data['15m'] = self.df_to_list(frames['15m'].loc[:t_idx].tail(200))
132
- ohlcv_data['1h'] = self.df_to_list(frames['1h'].loc[:t_idx].tail(200))
133
- ohlcv_data['4h'] = self.df_to_list(frames['4h'].loc[:t_idx].tail(100))
134
- ohlcv_data['1d'] = self.df_to_list(frames['1d'].loc[:t_idx].tail(50))
135
- except: continue
136
 
137
- if len(ohlcv_data['1h']) < 60: continue
138
-
139
- current_price = frames['5m'].loc[t_idx]['close']
 
 
140
 
141
- # 1. استدعاء شجرة المنطق (Logic Tree)
142
- logic_packet = {
143
- 'symbol': sym,
144
- 'ohlcv_1h': ohlcv_data['1h'][-60:],
145
- 'ohlcv_15m': ohlcv_data['15m'][-60:],
146
- 'change_24h': 0.0
147
- }
148
 
149
- try:
150
- if len(ohlcv_data['1h']) >= 24:
151
- p_now = ohlcv_data['1h'][-1][4]
152
- p_old = ohlcv_data['1h'][-24][4]
153
- logic_packet['change_24h'] = ((p_now - p_old) / p_old) * 100
154
- except: pass
 
 
 
 
 
 
 
 
155
 
156
- logic_result = self.dm._apply_logic_tree(logic_packet)
157
- signal_type = logic_result.get('type', 'NONE')
158
- l1_score = logic_result.get('score', 0.0)
159
-
160
- # 2. استدعاء Titan (Real AI)
161
- real_titan = 0.5
162
- if signal_type in ['BREAKOUT', 'REVERSAL']:
163
- raw_data_for_proc = {
164
  'symbol': sym,
165
- 'ohlcv': ohlcv_data, # ✅ الآن تحتوي على 1m
166
- 'current_price': current_price
 
167
  }
 
168
  try:
169
- proc_res = await self.proc.process_compound_signal(raw_data_for_proc)
170
- if proc_res:
171
- real_titan = proc_res.get('titan_score', 0.5)
 
172
  except: pass
173
 
174
- ai_results.append({
175
- 'timestamp': current_timestamp,
176
- 'symbol': sym,
177
- 'close': current_price,
178
- 'real_titan': real_titan,
179
- 'signal_type': signal_type,
180
- 'l1_score': l1_score
181
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
- if ai_results:
184
- pd.DataFrame(ai_results).to_pickle(scores_file)
185
- print(f" Saved ({len(ai_results)} signals).")
186
- else:
187
- print(" ⚠️ No signals.")
 
 
 
188
 
189
  # ==============================================================
190
  # PHASE 2: Portfolio Digital Twin Engine
 
1
  # ============================================================
2
+ # 🧪 backtest_engine.py (V71.2 - GEM-Architect: Memory-Safe Turbo)
3
  # ============================================================
4
 
5
  import asyncio
 
9
  import logging
10
  import itertools
11
  import os
12
+ import gc # ✅ مكتبة تنظيف الذاكرة
13
  import concurrent.futures
14
  from typing import Dict, Any, List
15
 
 
44
  ]
45
 
46
  if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR)
47
+ print(f"🧪 [Backtest V71.2] Memory-Safe Turbo Mode (GC Enabled).")
48
 
49
  # ==============================================================
50
  # 🛠️ Helpers
 
54
  return df[['timestamp', 'open', 'high', 'low', 'close', 'volume']].values.tolist()
55
 
56
  # ==============================================================
57
+ # PHASE 1: Generate Truth Data (With Memory Cleanup)
58
  # ==============================================================
59
  async def generate_truth_data(self):
60
  print(f"\n🚜 [Phase 1] Processing Logic Tree + Titan ({self.BACKTEST_DAYS} Days)...")
 
65
  safe_sym = sym.replace('/', '_')
66
  scores_file = f"{CACHE_DIR}/{safe_sym}_fullstack_scores.pkl"
67
 
68
+ # تنظيف الذاكرة قبل البدء بأي عملة جديدة
69
+ gc.collect()
70
+
71
  if os.path.exists(scores_file):
72
  print(f" 📂 {sym} scores ready. Skipping.")
73
  continue
74
 
75
  print(f" ⚙️ Simulating {sym}...", end="", flush=True)
76
 
77
+ # المتغيرات التي تحتاج تنظيف لاحقاً
78
  all_candles_1m = []
79
+ df_1m = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  frames = {}
81
 
82
+ try:
83
+ # 1. جلب بيانات الدقيقة
84
+ current_since = start_time_ms
85
+ while current_since < end_time_ms:
86
+ try:
87
+ batch = await self.dm.exchange.fetch_ohlcv(sym, '1m', since=current_since, limit=1000)
88
+ if not batch: break
89
+ last_ts = batch[-1][0]
90
+ if last_ts <= current_since: break
91
+ all_candles_1m.extend(batch)
92
+ current_since = last_ts + 1
93
+ # زيادة وقت الراحة قليلاً لإعطاء فرصة للنظام الحي
94
+ await asyncio.sleep(0.02)
95
+ if current_since >= end_time_ms: break
96
+ except: await asyncio.sleep(0.5)
97
+
98
+ all_candles_1m = [c for c in all_candles_1m if c[0] <= end_time_ms]
99
+ if not all_candles_1m:
100
+ print(" No Data.")
101
+ continue
102
 
103
+ df_1m = pd.DataFrame(all_candles_1m, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
104
+ df_1m['datetime'] = pd.to_datetime(df_1m['timestamp'], unit='ms')
105
+ df_1m.set_index('datetime', inplace=True)
106
+ df_1m = df_1m.sort_index()
 
 
 
107
 
108
+ # 🔥🔥🔥 Vectorization 🔥🔥🔥
109
+ agg_dict = {'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum'}
110
 
111
+ # 1m Direct
112
+ df_1m_ready = df_1m.copy()
113
+ df_1m_ready['timestamp'] = df_1m_ready.index.astype(np.int64) // 10**6
114
+ frames['1m'] = df_1m_ready
 
 
 
 
 
 
115
 
116
+ # Resampling
117
+ for tf_str, tf_code in [('5m', '5T'), ('15m', '15T'), ('1h', '1h'), ('4h', '4h'), ('1d', '1D')]:
118
+ resampled = df_1m.resample(tf_code).agg(agg_dict).dropna()
119
+ resampled['timestamp'] = resampled.index.astype(np.int64) // 10**6
120
+ frames[tf_str] = resampled
121
 
122
+ ai_results = []
123
+ valid_indices = frames['5m'].index[500:]
 
 
 
 
 
124
 
125
+ for t_idx in valid_indices:
126
+ # تحويل الوقت
127
+ current_timestamp = int(t_idx.timestamp() * 1000)
128
+
129
+ # 🔥 Fast Slicing
130
+ ohlcv_data = {}
131
+ try:
132
+ ohlcv_data['1m'] = self.df_to_list(frames['1m'].loc[:t_idx].tail(500))
133
+ ohlcv_data['5m'] = self.df_to_list(frames['5m'].loc[:t_idx].tail(200))
134
+ ohlcv_data['15m'] = self.df_to_list(frames['15m'].loc[:t_idx].tail(200))
135
+ ohlcv_data['1h'] = self.df_to_list(frames['1h'].loc[:t_idx].tail(200))
136
+ ohlcv_data['4h'] = self.df_to_list(frames['4h'].loc[:t_idx].tail(100))
137
+ ohlcv_data['1d'] = self.df_to_list(frames['1d'].loc[:t_idx].tail(50))
138
+ except: continue
139
 
140
+ if len(ohlcv_data['1h']) < 60: continue
141
+
142
+ current_price = frames['5m'].loc[t_idx]['close']
143
+
144
+ # 1. Logic Tree Check
145
+ logic_packet = {
 
 
146
  'symbol': sym,
147
+ 'ohlcv_1h': ohlcv_data['1h'][-60:],
148
+ 'ohlcv_15m': ohlcv_data['15m'][-60:],
149
+ 'change_24h': 0.0
150
  }
151
+
152
  try:
153
+ if len(ohlcv_data['1h']) >= 24:
154
+ p_now = ohlcv_data['1h'][-1][4]
155
+ p_old = ohlcv_data['1h'][-24][4]
156
+ logic_packet['change_24h'] = ((p_now - p_old) / p_old) * 100
157
  except: pass
158
 
159
+ logic_result = self.dm._apply_logic_tree(logic_packet)
160
+ signal_type = logic_result.get('type', 'NONE')
161
+ l1_score = logic_result.get('score', 0.0)
162
+
163
+ # 2. Titan AI Check
164
+ real_titan = 0.5
165
+ if signal_type in ['BREAKOUT', 'REVERSAL']:
166
+ raw_data_for_proc = {
167
+ 'symbol': sym,
168
+ 'ohlcv': ohlcv_data,
169
+ 'current_price': current_price
170
+ }
171
+ try:
172
+ # نستدعي Titan فقط عند الحاجة القصوى
173
+ proc_res = await self.proc.process_compound_signal(raw_data_for_proc)
174
+ if proc_res:
175
+ real_titan = proc_res.get('titan_score', 0.5)
176
+ except: pass
177
+
178
+ ai_results.append({
179
+ 'timestamp': current_timestamp,
180
+ 'symbol': sym,
181
+ 'close': current_price,
182
+ 'real_titan': real_titan,
183
+ 'signal_type': signal_type,
184
+ 'l1_score': l1_score
185
+ })
186
+
187
+ if ai_results:
188
+ pd.DataFrame(ai_results).to_pickle(scores_file)
189
+ print(f" ✅ Saved ({len(ai_results)} signals).")
190
+ else:
191
+ print(" ⚠️ No signals.")
192
+
193
+ except Exception as e:
194
+ print(f" ❌ Error: {e}")
195
 
196
+ finally:
197
+ # 🧹 CLEANUP MEMORY FORCEFULLY 🧹
198
+ # هذا الجزء يضمن عدم تراكم البيانات في الرام
199
+ del all_candles_1m
200
+ del df_1m
201
+ del frames
202
+ # استدعاء جامع القمامة يدوياً
203
+ gc.collect()
204
 
205
  # ==============================================================
206
  # PHASE 2: Portfolio Digital Twin Engine