Riy777 commited on
Commit
a9f0141
·
verified ·
1 Parent(s): 38daca3

Update ml_engine/data_manager.py

Browse files
Files changed (1) hide show
  1. ml_engine/data_manager.py +60 -56
ml_engine/data_manager.py CHANGED
@@ -1,6 +1,6 @@
1
  # ============================================================
2
  # 📂 ml_engine/data_manager.py
3
- # (V67.3 - GEM-Architect: Full Code - Relaxed Funnel)
4
  # ============================================================
5
 
6
  import asyncio
@@ -44,7 +44,7 @@ class DataManager:
44
  'UP', 'DOWN', 'BEAR', 'BULL', '3S', '3L'
45
  ]
46
 
47
- print(f"📦 [DataManager V67.3] Initialized (Relaxed Funnel).")
48
 
49
  async def initialize(self):
50
  print(" > [DataManager] Starting initialization...")
@@ -73,14 +73,16 @@ class DataManager:
73
  return self.contracts_db
74
 
75
  # ==================================================================
76
- # 🌍 Global Market Validator (The Gatekeeper)
77
  # ==================================================================
78
  async def check_global_market_health(self) -> Dict[str, Any]:
79
  """
80
- يفحص صحة السوق العامة (BTC) لتقرير ما إذا كان التداول آمناً.
 
 
81
  """
82
  try:
83
- # نعتمد على BTC كمؤشر للسوق
84
  btc_ohlcv = await self.exchange.fetch_ohlcv('BTC/USDT', '1d', limit=30)
85
  if not btc_ohlcv: return {'is_safe': True, 'reason': 'No BTC Data - Bypassed'}
86
 
@@ -88,52 +90,75 @@ class DataManager:
88
  current_close = df['c'].iloc[-1]
89
  prev_close = df['c'].iloc[-2]
90
 
91
- # 1. فحص الانهيار اليومي (Crash Check)
 
92
  daily_change = (current_close - prev_close) / prev_close
93
- if daily_change < -0.05: # انخفاض أكثر من 5% في يوم واحد
94
- return {'is_safe': False, 'reason': '🚨 BTC CRASH DETECTED (>5% Drop)'}
95
 
96
- # 2. فحص المتوسطات (Trend Check)
97
  sma20 = df['c'].rolling(20).mean().iloc[-1]
98
-
99
- # إذا كان السعر تحت متوسط 20 بفارق كبير، نعتبره خطراً
100
- dist_to_sma = (sma20 - current_close) / sma20
101
- if current_close < sma20 and dist_to_sma > 0.10: # بعيد جداً عن المتوسط لأسفل
102
- return {'is_safe': False, 'reason': '📉 Deep Bear Market (Below SMA20)'}
103
 
104
- # 3. فحص السيولة (Volume Check)
 
 
105
  avg_vol = df['v'].rolling(7).mean().iloc[-1]
106
  curr_vol = df['v'].iloc[-1]
107
 
108
- # إذا كان الفوليوم ضعيفاً جداً (سوق ميت)
109
- if curr_vol < (avg_vol * 0.3):
110
- return {'is_safe': False, 'reason': '💤 Dead Market / Low Volume'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
- return {'is_safe': True, 'reason': '✅ Market Stable'}
113
 
114
  except Exception as e:
115
  print(f"⚠️ [Market Validator] Error: {e}")
116
- # في حال الخطأ التقني، نفترض الأمان لتجنب التوقف الكامل
117
  return {'is_safe': True, 'reason': 'Error Bypass'}
118
 
119
  # ==================================================================
120
- # 🧠 Layer 1: Classification (Bottom, Momentum, Accumulation)
121
  # ==================================================================
122
  async def layer1_rapid_screening(self, adaptive_hub_ref=None) -> List[Dict[str, Any]]:
123
  self.adaptive_hub_ref = adaptive_hub_ref
124
- print(f"🔍 [Layer 1] Screening for High Vol Assets (Relaxed Funnel)...")
125
 
126
- # 0. فحص صحة السوق أولاً
127
  market_health = await self.check_global_market_health()
128
 
129
  if not market_health['is_safe']:
130
  print(f"⛔ [Market Validator] Trading Halted: {market_health['reason']}")
131
  return []
132
  else:
133
- # إضافة: طباعة حالة السوق عند النجاح
134
- print(f" 🌍 [Market Validator] Global Status: {market_health['reason']}")
135
 
136
- # 1. فلتر السيولة الأساسي (1 مليون دولار)
137
  initial_candidates = await self._stage0_universe_filter()
138
  if not initial_candidates:
139
  print("⚠️ [Layer 1] Stage 0 returned 0 candidates.")
@@ -145,39 +170,36 @@ class DataManager:
145
 
146
  semi_final_list = []
147
 
148
- # 3. التصنيف الفني الأولي
149
  for item in enriched_data:
150
  classification = self._classify_opportunity_type(item)
151
 
152
  if classification['type'] != 'NONE':
153
- # الاحتفاظ بتشخيص Regime القديم (للاستئناس فقط)
154
  regime_info = self._diagnose_asset_regime(item)
155
  item['asset_regime'] = regime_info['regime']
156
  item['asset_regime_conf'] = regime_info['conf']
157
 
158
- # الاعتماد الأساسي على التصنيف الجديد
159
  item['strategy_type'] = classification['type']
160
  item['l1_sort_score'] = classification['score']
161
  item['strategy_tag'] = classification['type']
162
 
163
- # فحص إضافي: إذا كان السوق ميتاً، نقبل فقط التجميع القوي
164
  if regime_info['regime'] == 'DEAD' and classification['type'] == 'MOMENTUM_LAUNCH':
165
  if not classification.get('is_squeeze', False):
166
  continue
167
 
168
  semi_final_list.append(item)
169
 
170
- # 4. 🧱 فحص عمق السوق وحقن الإعدادات
171
  final_list = []
172
- # نأخذ أفضل 300 مرشحاً لفحص دفتر الطلبات
173
  semi_final_list.sort(key=lambda x: x['l1_sort_score'], reverse=True)
174
  candidates_for_depth = semi_final_list[:300]
175
 
176
  if candidates_for_depth:
177
- print(f" 🛡️ [Layer 1.5] Checking Depth Support for {len(candidates_for_depth)} candidates...")
178
 
179
  for item in candidates_for_depth:
180
- # أ. فحص العمق (Order Book Check)
181
  if item['strategy_type'] in ['ACCUMULATION_SQUEEZE', 'SAFE_BOTTOM']:
182
  try:
183
  atr_val = item.get('atr_value', 0.0)
@@ -194,9 +216,8 @@ class DataManager:
194
  item['l1_sort_score'] -= 0.10
195
  except Exception: pass
196
 
197
- # ب. ✅ FIX: حقن الإعدادات باستخدام get_coin_type_config
198
  if self.adaptive_hub_ref:
199
- # نستخدم نوع العملة كمفتاح لجلب الإعدادات
200
  coin_type = item.get('strategy_type', 'SAFE_BOTTOM')
201
  dynamic_config = self.adaptive_hub_ref.get_coin_type_config(coin_type)
202
  item['dynamic_limits'] = dynamic_config
@@ -259,48 +280,40 @@ class DataManager:
259
  bb_width = (upper_bb - lower_bb) / curr['ema20'] if curr['ema20'] > 0 else 1.0
260
 
261
  # 🔥 1. Dead Coin Filter (Relaxed to 0.3%)
262
- # تم تخفيض العتبة من 0.4 إلى 0.3 للسماح للعملات الهادئة بالدخول إذا تحركت قليلاً
263
  volatility_pct = (atr / close) * 100 if close > 0 else 0
264
  if volatility_pct < 0.3: return {'type': 'NONE', 'score': 0}
265
 
266
  # 🛡️ TYPE 1: SAFE_BOTTOM (Widened)
267
- # RSI < 55 (كانت 45)، والسعر عند أو تحت الحد السفلي + 8% (كانت 5%)
268
  if rsi < 55:
269
- dist_from_ema = (ema50 - close) / ema50
270
  if close <= lower_bb * 1.08:
271
  score = (60 - rsi) / 20.0
272
  return {'type': 'SAFE_BOTTOM', 'score': min(score, 1.0)}
273
 
274
  # 🔋 TYPE 2: ACCUMULATION_SQUEEZE (Widened)
275
- # RSI 40-65 (كانت 45-60)، BB Width < 0.18 (كانت 0.12)
276
  elif 40 <= rsi <= 65:
277
  if bb_width < 0.18:
278
- # إزالة الشرط الصارم لـ EMA20 للسماح للنماذج بالاختيار
279
  score = 1.0 - (bb_width * 3.0)
280
  return {'type': 'ACCUMULATION_SQUEEZE', 'score': max(score, 0.5), 'is_squeeze': True}
281
 
282
  # 🚀 TYPE 3: MOMENTUM_LAUNCH (Earlier Entry)
283
- # RSI > 50 (كانت 60)
284
  elif 50 < rsi < 85:
285
  if close > ema50:
286
  dist_to_upper = (upper_bb - close) / close
287
- if dist_to_upper < 0.12: # السماح بمسافة أكبر قليلاً
288
  score = rsi / 100.0
289
  return {'type': 'MOMENTUM_LAUNCH', 'score': score}
290
 
291
- # 🃏 Special Case: High Volatility Wildcard
292
- # إذا كانت السيولة والتقلب مرتفعين جداً (> 1.5%)، مررها للنماذج
293
  if volatility_pct > 1.5:
294
  return {'type': 'SAFE_BOTTOM', 'score': 0.4}
295
 
296
  return {'type': 'NONE', 'score': 0}
297
 
298
  # ==================================================================
299
- # 🔍 Stage 0: Universe Filter (STRICT 1M FILTER)
300
  # ==================================================================
301
  async def _stage0_universe_filter(self) -> List[Dict[str, Any]]:
302
  try:
303
- # 🔥 إعداد الحد الأدنى لحجم التداول
304
  MIN_VOLUME_THRESHOLD = 1000000.0 # 1 Million USDT
305
 
306
  print(f" 🛡️ [Stage 0] Fetching Tickers (Min Vol: ${MIN_VOLUME_THRESHOLD:,.0f})...")
@@ -324,7 +337,6 @@ class DataManager:
324
 
325
  is_sovereign = symbol in SOVEREIGN_COINS
326
 
327
- # 🔥 الفلتر الصارم الجديد: رفض أي عملة تحت المليون
328
  if not is_sovereign:
329
  if calc_quote_vol < MIN_VOLUME_THRESHOLD:
330
  reject_stats["volume"] += 1
@@ -357,12 +369,8 @@ class DataManager:
357
  # 🧭 The Diagnoser
358
  # ------------------------------------------------------------------
359
  def _diagnose_asset_regime(self, item: Dict[str, Any]) -> Dict[str, Any]:
360
- """
361
- تقوم بتشخيص حالة السوق للأصل (Regime) لتحديد ما إذا كان مناسباً للدخول
362
- """
363
  try:
364
  if 'df_1h' not in item:
365
- # محاولة استخراج الداتا فريم إذا لم تكن موجودة
366
  if 'ohlcv_1h_raw' in item:
367
  item['df_1h'] = self._calc_indicators(item['ohlcv_1h_raw'])
368
  else:
@@ -415,7 +423,6 @@ class DataManager:
415
  c['ohlcv'] = {'1h': h1, '15m': m15}
416
  c['ohlcv_1h_raw'] = h1
417
  c['ohlcv_15m_raw'] = m15
418
- # حساب المؤشرات هنا لتوفير الوقت لاحقاً
419
  c['df_1h'] = self._calc_indicators(h1)
420
  return c
421
  except: return None
@@ -428,16 +435,13 @@ class DataManager:
428
  rs = gain/loss
429
  df['rsi'] = 100 - (100/(1+rs))
430
 
431
- # EMAs
432
  df['ema20'] = df['c'].ewm(span=20).mean()
433
  df['ema50'] = df['c'].ewm(span=50).mean()
434
  df['ema200'] = df['c'].ewm(span=200).mean()
435
 
436
- # ATR
437
  tr = np.maximum(df['h']-df['l'], np.maximum(abs(df['h']-df['c'].shift()), abs(df['l']-df['c'].shift())))
438
  df['atr'] = tr.rolling(14).mean()
439
 
440
- # Bollinger Bands
441
  std = df['c'].rolling(20).std()
442
  df['upper_bb'] = df['ema20'] + (2 * std)
443
  df['lower_bb'] = df['ema20'] - (2 * std)
 
1
  # ============================================================
2
  # 📂 ml_engine/data_manager.py
3
+ # (V67.5 - GEM-Architect: Smart Market Breadth Scanner)
4
  # ============================================================
5
 
6
  import asyncio
 
44
  'UP', 'DOWN', 'BEAR', 'BULL', '3S', '3L'
45
  ]
46
 
47
+ print(f"📦 [DataManager V67.5] Initialized (Smart Breadth Scanner).")
48
 
49
  async def initialize(self):
50
  print(" > [DataManager] Starting initialization...")
 
73
  return self.contracts_db
74
 
75
  # ==================================================================
76
+ # 🌍 Global Market Validator V2 (Smart Breadth Scanner)
77
  # ==================================================================
78
  async def check_global_market_health(self) -> Dict[str, Any]:
79
  """
80
+ يفحص صحة السوق العامة باستخدام منطق مزدوج:
81
+ 1. فحص سلامة BTC (تجنب الانهيارات).
82
+ 2. فحص نشاط العملات البديلة (Altcoin Pulse) للسماح بالتداول حتى لو كان BTC هادئاً.
83
  """
84
  try:
85
+ # 1. جلب بيانات البيتكوين الأساسية
86
  btc_ohlcv = await self.exchange.fetch_ohlcv('BTC/USDT', '1d', limit=30)
87
  if not btc_ohlcv: return {'is_safe': True, 'reason': 'No BTC Data - Bypassed'}
88
 
 
90
  current_close = df['c'].iloc[-1]
91
  prev_close = df['c'].iloc[-2]
92
 
93
+ # --- [ CRITICAL CHECK ] ---
94
+ # إذا كان البيتكوين ينهار، نوقف كل شيء لأن السيولة ستجف
95
  daily_change = (current_close - prev_close) / prev_close
96
+ if daily_change < -0.045: # السماح بمرونة أكبر قليلاً (-4.5%)
97
+ return {'is_safe': False, 'reason': f'🚨 BTC CRASHING ({daily_change*100:.2f}%)'}
98
 
99
+ # فحص المتوسطات (Trend Check)
100
  sma20 = df['c'].rolling(20).mean().iloc[-1]
101
+ if current_close < sma20 * 0.92: # إذا كان السعر تحت المتوسط بـ 8% (سوق هابط عنيف)
102
+ return {'is_safe': False, 'reason': '📉 Deep Bear Market (Risk Off)'}
 
 
 
103
 
104
+ # --- [ ALTCOIN PULSE CHECK ] ---
105
+ # بدلاً من إيقاف السوق بسبب ضعف فوليوم البيتكوين، نفحص هل هناك عملات تتحرك؟
106
+
107
  avg_vol = df['v'].rolling(7).mean().iloc[-1]
108
  curr_vol = df['v'].iloc[-1]
109
 
110
+ btc_is_dead = curr_vol < (avg_vol * 0.4) # البيتكوين ميت
111
+
112
+ if btc_is_dead:
113
+ # 🕵️ فحص النبض: هل هناك عملات تنفصل عن البيتكوين؟
114
+ print(" ⚠️ [Validator] BTC Volume Low.. Scanning Altcoin Pulse...")
115
+ tickers = await self.exchange.fetch_tickers()
116
+
117
+ green_coins = 0
118
+ pump_coins = 0
119
+ total_checked = 0
120
+
121
+ # نفحص العملات ذات الفوليوم العالي فقط
122
+ for sym, data in tickers.items():
123
+ if not sym.endswith('/USDT'): continue
124
+ vol = float(data.get('quoteVolume') or 0)
125
+ if vol < 5000000: continue # تجاهل العملات الصغيرة جداً في هذا الفحص
126
+
127
+ change = float(data.get('percentage') or 0)
128
+ total_checked += 1
129
+
130
+ if change > 1.0: green_coins += 1
131
+ if change > 5.0: pump_coins += 1
132
+
133
+ # المنطق: إذا وجدنا أكثر من 3 عملات تضخ بقوة، أو 40% من السوق أخضر -> السوق يعمل
134
+ if pump_coins >= 3 or (total_checked > 0 and (green_coins / total_checked) > 0.4):
135
+ return {'is_safe': True, 'reason': f'✅ Decoupled Alts Active ({pump_coins} Pumping)'}
136
+ else:
137
+ return {'is_safe': False, 'reason': '💤 Dead Market (BTC & Alts Flat)'}
138
 
139
+ return {'is_safe': True, 'reason': '✅ Market Healthy'}
140
 
141
  except Exception as e:
142
  print(f"⚠️ [Market Validator] Error: {e}")
 
143
  return {'is_safe': True, 'reason': 'Error Bypass'}
144
 
145
  # ==================================================================
146
+ # 🧠 Layer 1: Classification (Relaxed Funnel)
147
  # ==================================================================
148
  async def layer1_rapid_screening(self, adaptive_hub_ref=None) -> List[Dict[str, Any]]:
149
  self.adaptive_hub_ref = adaptive_hub_ref
150
+ print(f"🔍 [Layer 1] Screening Market (Smart Breadth)...")
151
 
152
+ # 0. فحص صحة السوق
153
  market_health = await self.check_global_market_health()
154
 
155
  if not market_health['is_safe']:
156
  print(f"⛔ [Market Validator] Trading Halted: {market_health['reason']}")
157
  return []
158
  else:
159
+ print(f" 🌍 [Market Validator] Status: {market_health['reason']}")
 
160
 
161
+ # 1. فلتر السيولة الأساسي
162
  initial_candidates = await self._stage0_universe_filter()
163
  if not initial_candidates:
164
  print("⚠️ [Layer 1] Stage 0 returned 0 candidates.")
 
170
 
171
  semi_final_list = []
172
 
173
+ # 3. التصنيف الفني
174
  for item in enriched_data:
175
  classification = self._classify_opportunity_type(item)
176
 
177
  if classification['type'] != 'NONE':
 
178
  regime_info = self._diagnose_asset_regime(item)
179
  item['asset_regime'] = regime_info['regime']
180
  item['asset_regime_conf'] = regime_info['conf']
181
 
 
182
  item['strategy_type'] = classification['type']
183
  item['l1_sort_score'] = classification['score']
184
  item['strategy_tag'] = classification['type']
185
 
186
+ # إذا كان التشخيص العاميت" لكن العملة في حالة ضغط (Squeeze)، نمررها
187
  if regime_info['regime'] == 'DEAD' and classification['type'] == 'MOMENTUM_LAUNCH':
188
  if not classification.get('is_squeeze', False):
189
  continue
190
 
191
  semi_final_list.append(item)
192
 
193
+ # 4. فحص العمق وحقن الإعدادات
194
  final_list = []
 
195
  semi_final_list.sort(key=lambda x: x['l1_sort_score'], reverse=True)
196
  candidates_for_depth = semi_final_list[:300]
197
 
198
  if candidates_for_depth:
199
+ print(f" 🛡️ [Layer 1.5] Checking Depth for {len(candidates_for_depth)} candidates...")
200
 
201
  for item in candidates_for_depth:
202
+ # أ. فحص العمق
203
  if item['strategy_type'] in ['ACCUMULATION_SQUEEZE', 'SAFE_BOTTOM']:
204
  try:
205
  atr_val = item.get('atr_value', 0.0)
 
216
  item['l1_sort_score'] -= 0.10
217
  except Exception: pass
218
 
219
+ # ب. حقن الإعدادات
220
  if self.adaptive_hub_ref:
 
221
  coin_type = item.get('strategy_type', 'SAFE_BOTTOM')
222
  dynamic_config = self.adaptive_hub_ref.get_coin_type_config(coin_type)
223
  item['dynamic_limits'] = dynamic_config
 
280
  bb_width = (upper_bb - lower_bb) / curr['ema20'] if curr['ema20'] > 0 else 1.0
281
 
282
  # 🔥 1. Dead Coin Filter (Relaxed to 0.3%)
 
283
  volatility_pct = (atr / close) * 100 if close > 0 else 0
284
  if volatility_pct < 0.3: return {'type': 'NONE', 'score': 0}
285
 
286
  # 🛡️ TYPE 1: SAFE_BOTTOM (Widened)
 
287
  if rsi < 55:
 
288
  if close <= lower_bb * 1.08:
289
  score = (60 - rsi) / 20.0
290
  return {'type': 'SAFE_BOTTOM', 'score': min(score, 1.0)}
291
 
292
  # 🔋 TYPE 2: ACCUMULATION_SQUEEZE (Widened)
 
293
  elif 40 <= rsi <= 65:
294
  if bb_width < 0.18:
 
295
  score = 1.0 - (bb_width * 3.0)
296
  return {'type': 'ACCUMULATION_SQUEEZE', 'score': max(score, 0.5), 'is_squeeze': True}
297
 
298
  # 🚀 TYPE 3: MOMENTUM_LAUNCH (Earlier Entry)
 
299
  elif 50 < rsi < 85:
300
  if close > ema50:
301
  dist_to_upper = (upper_bb - close) / close
302
+ if dist_to_upper < 0.12:
303
  score = rsi / 100.0
304
  return {'type': 'MOMENTUM_LAUNCH', 'score': score}
305
 
306
+ # 🃏 Special Case
 
307
  if volatility_pct > 1.5:
308
  return {'type': 'SAFE_BOTTOM', 'score': 0.4}
309
 
310
  return {'type': 'NONE', 'score': 0}
311
 
312
  # ==================================================================
313
+ # 🔍 Stage 0: Universe Filter
314
  # ==================================================================
315
  async def _stage0_universe_filter(self) -> List[Dict[str, Any]]:
316
  try:
 
317
  MIN_VOLUME_THRESHOLD = 1000000.0 # 1 Million USDT
318
 
319
  print(f" 🛡️ [Stage 0] Fetching Tickers (Min Vol: ${MIN_VOLUME_THRESHOLD:,.0f})...")
 
337
 
338
  is_sovereign = symbol in SOVEREIGN_COINS
339
 
 
340
  if not is_sovereign:
341
  if calc_quote_vol < MIN_VOLUME_THRESHOLD:
342
  reject_stats["volume"] += 1
 
369
  # 🧭 The Diagnoser
370
  # ------------------------------------------------------------------
371
  def _diagnose_asset_regime(self, item: Dict[str, Any]) -> Dict[str, Any]:
 
 
 
372
  try:
373
  if 'df_1h' not in item:
 
374
  if 'ohlcv_1h_raw' in item:
375
  item['df_1h'] = self._calc_indicators(item['ohlcv_1h_raw'])
376
  else:
 
423
  c['ohlcv'] = {'1h': h1, '15m': m15}
424
  c['ohlcv_1h_raw'] = h1
425
  c['ohlcv_15m_raw'] = m15
 
426
  c['df_1h'] = self._calc_indicators(h1)
427
  return c
428
  except: return None
 
435
  rs = gain/loss
436
  df['rsi'] = 100 - (100/(1+rs))
437
 
 
438
  df['ema20'] = df['c'].ewm(span=20).mean()
439
  df['ema50'] = df['c'].ewm(span=50).mean()
440
  df['ema200'] = df['c'].ewm(span=200).mean()
441
 
 
442
  tr = np.maximum(df['h']-df['l'], np.maximum(abs(df['h']-df['c'].shift()), abs(df['l']-df['c'].shift())))
443
  df['atr'] = tr.rolling(14).mean()
444
 
 
445
  std = df['c'].rolling(20).std()
446
  df['upper_bb'] = df['ema20'] + (2 * std)
447
  df['lower_bb'] = df['ema20'] - (2 * std)