Riy777 commited on
Commit
a2a5dbf
·
verified ·
1 Parent(s): 01fca6c

Create backtest_engine.py

Browse files
Files changed (1) hide show
  1. backtest_engine.py +280 -0
backtest_engine.py ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # 🧪 backtest_engine.py (V36.1 - GEM-Architect: Full Spectrum)
3
+ # ============================================================
4
+ # التحديثات:
5
+ # 1. إدراج Sniper في المحاكاة (ML Only Mode).
6
+ # 2. تحييد وزن دفتر الطلبات داخل Sniper مؤقتاً أثناء الاختبار.
7
+ # 3. تحسين نظام التوليد (Resampling) ليكون Async بالكامل.
8
+ # ============================================================
9
+
10
+ import asyncio
11
+ import pandas as pd
12
+ import numpy as np
13
+ import json
14
+ import time
15
+ from datetime import datetime, timedelta
16
+
17
+ # استيراد المكونات الأساسية
18
+ from ml_engine.processor import MLProcessor, SystemLimits
19
+ from ml_engine.data_manager import DataManager
20
+ from learning_hub.adaptive_hub import StrategyDNA
21
+
22
+ class BacktestSimulator:
23
+ def __init__(self, data_manager, processor):
24
+ self.dm = data_manager
25
+ self.proc = processor
26
+ self.history_cache = {} # {symbol: DataFrame_1m_Full}
27
+
28
+ # إعدادات المحاكاة
29
+ self.DAYS_TO_FETCH = 30
30
+ self.CHUNK_LIMIT = 1500
31
+
32
+ print("🧪 [Backtest Engine V36.1] Full-Spectrum Simulation Initialized.")
33
+
34
+ # ==========================================================================
35
+ # 1. نظام جلب البيانات (كما هو)
36
+ # ==========================================================================
37
+ async def fetch_deep_history_1m(self, symbols: list):
38
+ """جلب شهر كامل بدقة دقيقة واحدة"""
39
+ print(f"⏳ [Backtest] Fetching 1m candles for {self.DAYS_TO_FETCH} days...")
40
+ end_time = int(time.time() * 1000)
41
+ start_time = end_time - (self.DAYS_TO_FETCH * 24 * 60 * 60 * 1000)
42
+
43
+ for sym in symbols:
44
+ print(f" 📥 Downloading {sym}...", end="", flush=True)
45
+ all_candles = []
46
+ current_end = end_time
47
+
48
+ while current_end > start_time:
49
+ candles = await self.dm.exchange.fetch_ohlcv(
50
+ sym, '1m', limit=self.CHUNK_LIMIT, params={'endAt': int(current_end / 1000)}
51
+ )
52
+ if not candles: break
53
+
54
+ first_candle_time = candles[0][0]
55
+ current_end = first_candle_time - 60000
56
+ all_candles = candles + all_candles
57
+ await asyncio.sleep(0.05)
58
+
59
+ if all_candles:
60
+ df = pd.DataFrame(all_candles, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
61
+ df['datetime'] = pd.to_datetime(df['timestamp'], unit='ms')
62
+ df = df.set_index('datetime').sort_index()
63
+ mask = (df.index >= pd.to_datetime(start_time, unit='ms')) & (df.index <= pd.to_datetime(end_time, unit='ms'))
64
+ self.history_cache[sym] = df.loc[mask]
65
+ print(f" ✅ ({len(self.history_cache[sym])} candles)")
66
+ else:
67
+ print(" ❌ No Data")
68
+
69
+ # ==========================================================================
70
+ # 2. نظام توليد الإطارات (Resampling)
71
+ # ==========================================================================
72
+ def resample_data(self, df_1m, end_idx):
73
+ """توليد الهرم المقلوب من البيانات"""
74
+ start_pos = max(0, end_idx - 1000) # يكفي 1000 دقيقة للوراء
75
+ slice_1m = df_1m.iloc[start_pos : end_idx].copy()
76
+
77
+ if slice_1m.empty or len(slice_1m) < 60: return None
78
+
79
+ timeframes = {'1m': slice_1m.reset_index(drop=True).values.tolist()}
80
+ agg_dict = {'timestamp': 'first', 'open': 'first', 'high': 'max', 'low': 'min', 'close': 'last', 'volume': 'sum'}
81
+
82
+ try:
83
+ for tf, rule in [('5m', '5T'), ('15m', '15T'), ('1h', '1H'), ('4h', '4H')]:
84
+ resampled = slice_1m.resample(rule, on='datetime').agg(agg_dict).dropna()
85
+ timeframes[tf] = resampled.reset_index(drop=True).values.tolist()
86
+ return timeframes
87
+ except: return None
88
+
89
+ # ==========================================================================
90
+ # 3. محرك المحاكاة (Async Simulation Core)
91
+ # ==========================================================================
92
+ async def run_regime_simulation(self, regime_name: str, candidate_weights: dict):
93
+ """
94
+ تشغيل المحاكاة مع دمج Sniper في المعادلة
95
+ """
96
+ # 1. حقن الأوزان في الدستور (SystemLimits)
97
+ SystemLimits.CURRENT_REGIME = regime_name
98
+ SystemLimits.L2_WEIGHT_TITAN = candidate_weights['titan']
99
+ SystemLimits.L2_WEIGHT_PATTERNS = candidate_weights['patterns']
100
+ SystemLimits.L2_WEIGHT_MC = candidate_weights['mc']
101
+
102
+ # 2. تكوين Sniper لوضع "الباكتست" (تجاهل دفتر الطلبات المفقود)
103
+ if self.proc.sniper:
104
+ self.proc.sniper.configure_settings(
105
+ threshold=0.4, # عتبة افتراضية
106
+ wall_ratio=0.9, # تساهل تام مع الجدران (لأنها غير موجودة)
107
+ w_ml=1.0, # 🔥 الاعتماد الكلي على ML
108
+ w_ob=0.0 # إلغاء وزن دفتر الطلبات
109
+ )
110
+
111
+ capital = 1000.0
112
+ trades = 0
113
+ wins = 0
114
+
115
+ print(f" 🧪 Testing Weights: {candidate_weights} ...")
116
+
117
+ for sym, df_1m_full in self.history_cache.items():
118
+ if df_1m_full.empty: continue
119
+
120
+ # نمشي بخطوات 30 دقيقة (تسريعاً للعملية)
121
+ step = 30
122
+ for i in range(1000, len(df_1m_full) - 240, step):
123
+
124
+ ohlcv_multi = self.resample_data(df_1m_full, i)
125
+ if not ohlcv_multi: continue
126
+
127
+ current_price = df_1m_full.iloc[i]['close']
128
+
129
+ # --- A. Titan Prediction ---
130
+ titan_score = 0.5
131
+ if self.proc.titan:
132
+ res = await asyncio.to_thread(self.proc.titan.predict, ohlcv_multi)
133
+ titan_score = res.get('score', 0.5)
134
+
135
+ # --- B. Patterns Prediction ---
136
+ pattern_score = 0.5
137
+ if self.proc.patterns:
138
+ self.proc.patterns.configure_thresholds(
139
+ weights=SystemLimits.PATTERN_TF_WEIGHTS,
140
+ bull_thresh=SystemLimits.PATTERN_THRESH_BULLISH,
141
+ bear_thresh=SystemLimits.PATTERN_THRESH_BEARISH
142
+ )
143
+ res = await self.proc.patterns.detect_chart_patterns(ohlcv_multi)
144
+ pattern_score = res.get('pattern_confidence', 0.5)
145
+
146
+ # --- C. Sniper Prediction (ML Only) ---
147
+ sniper_score = 0.5
148
+ if self.proc.sniper:
149
+ # Sniper يحتاج قائمة 1m
150
+ res = await self.proc.sniper.check_entry_signal_async(ohlcv_multi['1m'], order_book_data=None)
151
+ sniper_score = res.get('ml_score', 0.5) # نأخذ ml_score الخام
152
+
153
+ # --- D. Monte Carlo ---
154
+ mc_score = 0.5
155
+ if self.proc.mc_analyzer:
156
+ closes_1h = [c[4] for c in ohlcv_multi['1h']]
157
+ raw_mc = self.proc.mc_analyzer.run_light_check(closes_1h)
158
+ mc_score = 0.5 + (raw_mc * 5.0)
159
+
160
+ # --- E. حساب النتيجة الموزونة (Weighted Score) ---
161
+ w_t = candidate_weights['titan']
162
+ w_p = candidate_weights['patterns']
163
+ w_s = candidate_weights['sniper'] # ✅ الوزن الجديد
164
+ w_m = candidate_weights['mc']
165
+
166
+ total = w_t + w_p + w_s + w_m
167
+ final_score = ((titan_score * w_t) + (pattern_score * w_p) + (sniper_score * w_s) + (mc_score * w_m)) / total
168
+
169
+ # قرار الدخول
170
+ if final_score > 0.75:
171
+ # فحص النتيجة بعد 4 ساعات
172
+ future_idx = i + 240 # 4 ساعات * 60 دقيقة
173
+ if future_idx >= len(df_1m_full): break
174
+
175
+ future_price = df_1m_full.iloc[future_idx]['close']
176
+ change = (future_price - current_price) / current_price
177
+
178
+ if change > 0.02: # ربح 2%
179
+ capital *= 1.02
180
+ wins += 1
181
+ i += 120 # قفزة لتجنب التكرار
182
+ elif change < -0.015: # خسارة 1.5%
183
+ capital *= 0.985
184
+ i += 120
185
+
186
+ trades += 1
187
+
188
+ win_rate = (wins / trades * 100) if trades > 0 else 0
189
+ return {'final_capital': capital, 'win_rate': win_rate, 'trades': trades}
190
+
191
+ # ==========================================================================
192
+ # 4. المايسترو (Genetic Optimizer with Sniper)
193
+ # ==========================================================================
194
+ async def optimize_dna(self):
195
+ best_dna = {}
196
+ regimes = ['BULL', 'BEAR', 'RANGE'] # DEAD يحتاج استراتيجية خاصة
197
+
198
+ # مساحة البحث (تشمل الآن Sniper)
199
+ search_space = [
200
+ # 1. متوازن
201
+ {'titan': 0.3, 'patterns': 0.3, 'sniper': 0.3, 'mc': 0.1},
202
+ # 2. تركيز على Titan (Trend)
203
+ {'titan': 0.6, 'patterns': 0.2, 'sniper': 0.1, 'mc': 0.1},
204
+ # 3. تركيز على Sniper (Scalping/Momentum)
205
+ {'titan': 0.2, 'patterns': 0.2, 'sniper': 0.5, 'mc': 0.1},
206
+ # 4. تركيز على Patterns (Reversal)
207
+ {'titan': 0.2, 'patterns': 0.6, 'sniper': 0.1, 'mc': 0.1},
208
+ ]
209
+
210
+ for regime in regimes:
211
+ print(f"\n🧬 Optimizing DNA for {regime}...")
212
+ best_score = -9999
213
+ best_w = None
214
+
215
+ for weights in search_space:
216
+ # تشغيل المحاكاة (Async مباشرة)
217
+ res = await self.run_regime_simulation(regime, weights)
218
+
219
+ score = res['final_capital']
220
+ print(f" -> {weights} => Cap: ${score:.1f} | WR: {res['win_rate']:.1f}% ({res['trades']} trds)")
221
+
222
+ if score > best_score:
223
+ best_score = score
224
+ best_w = weights
225
+
226
+ # حفظ أفضل إعدادات (مع إضافة Hydra كوزن ثابت لأنه خروج أكثر منه دخول)
227
+ if best_w:
228
+ best_dna[regime] = {
229
+ "model_weights": {
230
+ "titan": best_w['titan'],
231
+ "patterns": best_w['patterns'],
232
+ "sniper": best_w['sniper'],
233
+ "hydra": 0.1, # Hydra يضاف كوزن ثابت للدخول (أو يترك للحراس)
234
+ "mc": best_w['mc']
235
+ },
236
+ "ob_settings": {"wall_ratio_limit": 0.4, "imbalance_thresh": 0.5},
237
+ "filters": {"l1_min_score": 15.0, "l3_conf_thresh": 0.65}
238
+ }
239
+ print(f"🏆 WINNER for {regime}: Ti={best_w['titan']}, Sn={best_w['sniper']}")
240
+
241
+ return best_dna
242
+
243
+ # ============================================================
244
+ # 🚀 نقطة التشغيل
245
+ # ============================================================
246
+ async def run_strategic_optimization_task():
247
+ print("\n🧪 [STRATEGIC BACKTEST] Starting Sequence...")
248
+
249
+ # 1. تهيئة الخدمات
250
+ from r2 import R2Service
251
+ r2 = R2Service()
252
+ dm = DataManager(None, None, r2)
253
+ await dm.initialize()
254
+ proc = MLProcessor(dm)
255
+ await proc.initialize()
256
+
257
+ sim = BacktestSimulator(dm, proc)
258
+
259
+ # 2. تحميل البيانات
260
+ benchmark_coins = ['BTC/USDT', 'ETH/USDT', 'SOL/USDT', 'XRP/USDT']
261
+ await sim.fetch_deep_history_1m(benchmark_coins)
262
+
263
+ # 3. التشغيل والتحسين
264
+ optimized_strategies = await sim.optimize_dna()
265
+
266
+ # 4. حفظ النتائج وتحديث AdaptiveHub
267
+ from learning_hub.adaptive_hub import AdaptiveHub
268
+ hub = AdaptiveHub(r2)
269
+ await hub.initialize()
270
+
271
+ for reg, data in optimized_strategies.items():
272
+ if reg in hub.strategies:
273
+ hub.strategies[reg].model_weights.update(data['model_weights'])
274
+
275
+ await hub._save_state_to_r2()
276
+ await dm.close()
277
+ print("✅ [STRATEGIC BACKTEST] Completed Successfully.")
278
+
279
+ if __name__ == "__main__":
280
+ asyncio.run(run_strategic_optimization_task())