Riy777 commited on
Commit
c7a21ab
·
verified ·
1 Parent(s): 4ddd041

Update ml_engine/data_manager.py

Browse files
Files changed (1) hide show
  1. ml_engine/data_manager.py +92 -40
ml_engine/data_manager.py CHANGED
@@ -1,6 +1,6 @@
1
  # ============================================================
2
  # 📂 ml_engine/data_manager.py
3
- # (V60.1 - GEM-Architect: Tuned Anti-FOMO Shield)
4
  # ============================================================
5
 
6
  import asyncio
@@ -12,11 +12,13 @@ import pandas as pd
12
  import numpy as np
13
  from typing import List, Dict, Any
14
 
 
15
  try:
16
  from ml_engine.processor import SystemLimits
17
  except ImportError:
18
  SystemLimits = None
19
 
 
20
  logging.getLogger("httpx").setLevel(logging.WARNING)
21
  logging.getLogger("ccxt").setLevel(logging.WARNING)
22
 
@@ -25,7 +27,7 @@ class DataManager:
25
  self.contracts_db = contracts_db or {}
26
  self.whale_monitor = whale_monitor
27
  self.r2_service = r2_service
28
- self.adaptive_hub_ref = None
29
 
30
  self.exchange = ccxt.kucoin({
31
  'enableRateLimit': True,
@@ -36,12 +38,13 @@ class DataManager:
36
  self.http_client = None
37
  self.market_cache = {}
38
 
 
39
  self.BLACKLIST_TOKENS = [
40
  'USDT', 'USDC', 'DAI', 'TUSD', 'BUSD', 'FDUSD', 'EUR', 'PAX',
41
  'UP', 'DOWN', 'BEAR', 'BULL', '3S', '3L'
42
  ]
43
 
44
- print(f"📦 [DataManager V60.1] Tuned Shield + Context Injector Active.")
45
 
46
  async def initialize(self):
47
  print(" > [DataManager] Starting initialization...")
@@ -74,23 +77,23 @@ class DataManager:
74
  # ==================================================================
75
  async def layer1_rapid_screening(self, adaptive_hub_ref=None) -> List[Dict[str, Any]]:
76
  self.adaptive_hub_ref = adaptive_hub_ref
77
- print(f"🔍 [Layer 1] Initiating Tuned Screening...")
78
 
79
- # 1. فلتر السيولة الأساسي (تم تخفيفه)
80
  initial_candidates = await self._stage0_universe_filter()
81
 
82
  if not initial_candidates:
83
- print("⚠️ [Layer 1] Stage 0 returned 0 candidates. Check Quote Volume Limit.")
84
  return []
85
 
86
- # 2. جلب البيانات الفنية
87
- top_candidates = initial_candidates[:120] # وسعنا النطاق قليلاً
88
  enriched_data = await self._fetch_technical_data_batch(top_candidates)
89
 
90
  final_list = []
91
 
92
  for item in enriched_data:
93
- # 3. تطبيق الفلتر (تم تخفيف القيود)
94
  classification = self._apply_strict_logic_tree(item)
95
 
96
  if classification['type'] != 'NONE':
@@ -99,6 +102,7 @@ class DataManager:
99
  item['asset_regime'] = regime_info['regime']
100
  item['asset_regime_conf'] = regime_info['conf']
101
 
 
102
  if self.adaptive_hub_ref:
103
  dynamic_config = self.adaptive_hub_ref.get_regime_config(regime_info['regime'])
104
  item['dynamic_limits'] = dynamic_config
@@ -114,6 +118,79 @@ class DataManager:
114
  print(f"✅ [Layer 1] Passed {len(selection)} candidates to Processor.")
115
  return selection
116
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  # ------------------------------------------------------------------
118
  # 🧭 The Diagnoser
119
  # ------------------------------------------------------------------
@@ -144,7 +221,7 @@ class DataManager:
144
  except Exception: return {'regime': 'RANGE', 'conf': 0.0}
145
 
146
  # ------------------------------------------------------------------
147
- # 🛡️ The Logic Tree (TUNED for better flow)
148
  # ------------------------------------------------------------------
149
  def _apply_strict_logic_tree(self, data: Dict[str, Any]) -> Dict[str, Any]:
150
  try:
@@ -162,13 +239,13 @@ class DataManager:
162
  except: change_4h = 0.0
163
 
164
  # 🔧 TUNED GATES (فتحات التنفس)
165
- # 1. السماح بحركة 12% في 4 ساعات (بدلاً من 8%)
166
  if change_4h > 12.0: return {'type': 'NONE', 'score': 0}
167
 
168
- # 2. السماح بـ RSI حتى 75 (بدلاً من 70)
169
  if curr_1h['rsi'] > 75: return {'type': 'NONE', 'score': 0}
170
 
171
- # 3. السماح بانحراف 2.2 ATR (بدلاً من 1.8)
172
  dev = (curr_1h['close'] - curr_1h['ema20']) / curr_1h['atr'] if curr_1h['atr'] > 0 else 0
173
  if dev > 2.2: return {'type': 'NONE', 'score': 0}
174
 
@@ -176,10 +253,10 @@ class DataManager:
176
 
177
  # A. Breakout (اختراق آمن)
178
  is_bullish = (curr_1h['ema20'] > curr_1h['ema50']) or (curr_1h['close'] > curr_1h['ema20'])
179
- if is_bullish and (45 <= curr_1h['rsi'] <= 72): # وسعنا نطاق الـ RSI قليلاً
180
  vol_ma = df_15m['volume'].rolling(20).mean().iloc[-1]
181
 
182
- # 🔧 TUNED: خفضنا شرط الفوليوم لـ 1.2x (بدلاً من 1.5x) لالتقاط بداية الحركة
183
  if curr_15m['volume'] >= 1.2 * vol_ma:
184
  score = curr_15m['volume'] / vol_ma if vol_ma > 0 else 1.0
185
  return {'type': 'BREAKOUT', 'score': score}
@@ -194,31 +271,6 @@ class DataManager:
194
  # ------------------------------------------------------------------
195
  # Helpers
196
  # ------------------------------------------------------------------
197
- async def _stage0_universe_filter(self) -> List[Dict[str, Any]]:
198
- try:
199
- tickers = await self.exchange.fetch_tickers()
200
- candidates = []
201
- for symbol, ticker in tickers.items():
202
- if not symbol.endswith('/USDT'): continue
203
- if any(b in symbol for b in self.BLACKLIST_TOKENS): continue
204
-
205
- # 🔧 TUNED: خفضنا الحد الأدنى للسيولة لـ 500k
206
- quote_vol = ticker.get('quoteVolume', 0)
207
- if quote_vol < 500_000: continue
208
-
209
- # 🔧 TUNED: السماح بـ 20% تغير يومي (بدلاً من 15%)
210
- if ticker.get('percentage', 0) > 20.0: continue
211
-
212
- candidates.append({
213
- 'symbol': symbol,
214
- 'quote_volume': quote_vol,
215
- 'current_price': ticker.get('last'),
216
- 'change_24h': ticker.get('percentage')
217
- })
218
- candidates.sort(key=lambda x: x['quote_volume'], reverse=True)
219
- return candidates
220
- except: return []
221
-
222
  async def _fetch_technical_data_batch(self, candidates):
223
  chunk_size = 10; results = []
224
  for i in range(0, len(candidates), chunk_size):
 
1
  # ============================================================
2
  # 📂 ml_engine/data_manager.py
3
+ # (V60.2 - GEM-Architect: Robust Data Fix & Tuned Shield)
4
  # ============================================================
5
 
6
  import asyncio
 
12
  import numpy as np
13
  from typing import List, Dict, Any
14
 
15
+ # محاولة استيراد حدود النظام (اختياري)
16
  try:
17
  from ml_engine.processor import SystemLimits
18
  except ImportError:
19
  SystemLimits = None
20
 
21
+ # إعدادات التسجيل لتقليل الضوضاء
22
  logging.getLogger("httpx").setLevel(logging.WARNING)
23
  logging.getLogger("ccxt").setLevel(logging.WARNING)
24
 
 
27
  self.contracts_db = contracts_db or {}
28
  self.whale_monitor = whale_monitor
29
  self.r2_service = r2_service
30
+ self.adaptive_hub_ref = None # مرجع لملف التعلم
31
 
32
  self.exchange = ccxt.kucoin({
33
  'enableRateLimit': True,
 
38
  self.http_client = None
39
  self.market_cache = {}
40
 
41
+ # قائمة العملات المحظورة
42
  self.BLACKLIST_TOKENS = [
43
  'USDT', 'USDC', 'DAI', 'TUSD', 'BUSD', 'FDUSD', 'EUR', 'PAX',
44
  'UP', 'DOWN', 'BEAR', 'BULL', '3S', '3L'
45
  ]
46
 
47
+ print(f"📦 [DataManager V60.2] Robust Data Fix + Tuned Shield Active.")
48
 
49
  async def initialize(self):
50
  print(" > [DataManager] Starting initialization...")
 
77
  # ==================================================================
78
  async def layer1_rapid_screening(self, adaptive_hub_ref=None) -> List[Dict[str, Any]]:
79
  self.adaptive_hub_ref = adaptive_hub_ref
80
+ print(f"🔍 [Layer 1] Initiating Robust Screening...")
81
 
82
+ # 1. فلتر السيولة الأساسي (المصحح)
83
  initial_candidates = await self._stage0_universe_filter()
84
 
85
  if not initial_candidates:
86
+ print("⚠️ [Layer 1] CRITICAL: Stage 0 returned 0 candidates.")
87
  return []
88
 
89
+ # 2. جلب البيانات الفنية (لأفضل 120 عملة لضمان التنوع)
90
+ top_candidates = initial_candidates[:120]
91
  enriched_data = await self._fetch_technical_data_batch(top_candidates)
92
 
93
  final_list = []
94
 
95
  for item in enriched_data:
96
+ # 3. تطبيق الفلتر الفني (Tuned Anti-FOMO)
97
  classification = self._apply_strict_logic_tree(item)
98
 
99
  if classification['type'] != 'NONE':
 
102
  item['asset_regime'] = regime_info['regime']
103
  item['asset_regime_conf'] = regime_info['conf']
104
 
105
+ # حقن العتبات الديناميكية
106
  if self.adaptive_hub_ref:
107
  dynamic_config = self.adaptive_hub_ref.get_regime_config(regime_info['regime'])
108
  item['dynamic_limits'] = dynamic_config
 
118
  print(f"✅ [Layer 1] Passed {len(selection)} candidates to Processor.")
119
  return selection
120
 
121
+ # ==================================================================
122
+ # 🔍 Stage 0: Universe Filter (Debugged & Robust)
123
+ # ==================================================================
124
+ async def _stage0_universe_filter(self) -> List[Dict[str, Any]]:
125
+ try:
126
+ print(" 🛡️ [Stage 0] Fetching Tickers...")
127
+ tickers = await self.exchange.fetch_tickers()
128
+ candidates = []
129
+
130
+ # عداد لمعرفة سبب الرفض (للتشخيص)
131
+ reject_stats = {"volume": 0, "change": 0, "blacklist": 0, "bad_data": 0}
132
+ debug_printed = False
133
+
134
+ for symbol, ticker in tickers.items():
135
+ if not symbol.endswith('/USDT'): continue
136
+
137
+ # 1. Blacklist
138
+ base_curr = symbol.split('/')[0]
139
+ if any(bad in base_curr for bad in self.BLACKLIST_TOKENS):
140
+ reject_stats["blacklist"] += 1
141
+ continue
142
+
143
+ # 2. Robust Volume Calculation (الإصلاح الجذري)
144
+ quote_vol = ticker.get('quoteVolume')
145
+
146
+ # إذا ��ان غير موجود أو صفر، نحسبه يدوياً
147
+ if quote_vol is None or float(quote_vol) == 0:
148
+ base_vol = ticker.get('baseVolume')
149
+ last_price = ticker.get('last')
150
+ if base_vol is not None and last_price is not None:
151
+ quote_vol = float(base_vol) * float(last_price)
152
+ else:
153
+ quote_vol = 0.0
154
+
155
+ quote_vol = float(quote_vol)
156
+
157
+ # طباعة فحص لعملة معروفة (BTC) للتأكد من صحة البيانات في السجل
158
+ if "BTC/USDT" in symbol and not debug_printed:
159
+ print(f" 🐛 [DEBUG] BTC Data Check: Vol=${quote_vol:,.2f} | Change={ticker.get('percentage')}%")
160
+ debug_printed = True
161
+
162
+ # فلتر السيولة (500k)
163
+ if quote_vol < 500_000:
164
+ reject_stats["volume"] += 1
165
+ continue
166
+
167
+ # فلتر التغير (20%)
168
+ change_pct = ticker.get('percentage')
169
+ if change_pct is None: change_pct = 0.0
170
+
171
+ if abs(change_pct) > 20.0:
172
+ reject_stats["change"] += 1
173
+ continue
174
+
175
+ candidates.append({
176
+ 'symbol': symbol,
177
+ 'quote_volume': quote_vol,
178
+ 'current_price': ticker.get('last'),
179
+ 'change_24h': change_pct
180
+ })
181
+
182
+ # طباعة تقرير الرفض
183
+ print(f" 📊 [Filter Stats] Total Tickers: {len(tickers)} | Passed: {len(candidates)}")
184
+ print(f" ❌ Rejected: Vol < 500k ({reject_stats['volume']}) | Change > 20% ({reject_stats['change']})")
185
+
186
+ candidates.sort(key=lambda x: x['quote_volume'], reverse=True)
187
+ return candidates
188
+
189
+ except Exception as e:
190
+ print(f"❌ [L1 Error] Universe filter failed: {e}")
191
+ traceback.print_exc()
192
+ return []
193
+
194
  # ------------------------------------------------------------------
195
  # 🧭 The Diagnoser
196
  # ------------------------------------------------------------------
 
221
  except Exception: return {'regime': 'RANGE', 'conf': 0.0}
222
 
223
  # ------------------------------------------------------------------
224
+ # 🛡️ The Logic Tree (TUNED)
225
  # ------------------------------------------------------------------
226
  def _apply_strict_logic_tree(self, data: Dict[str, Any]) -> Dict[str, Any]:
227
  try:
 
239
  except: change_4h = 0.0
240
 
241
  # 🔧 TUNED GATES (فتحات التنفس)
242
+ # 1. السماح بحركة 12% في 4 ساعات
243
  if change_4h > 12.0: return {'type': 'NONE', 'score': 0}
244
 
245
+ # 2. السماح بـ RSI حتى 75
246
  if curr_1h['rsi'] > 75: return {'type': 'NONE', 'score': 0}
247
 
248
+ # 3. السماح بانحراف 2.2 ATR
249
  dev = (curr_1h['close'] - curr_1h['ema20']) / curr_1h['atr'] if curr_1h['atr'] > 0 else 0
250
  if dev > 2.2: return {'type': 'NONE', 'score': 0}
251
 
 
253
 
254
  # A. Breakout (اختراق آمن)
255
  is_bullish = (curr_1h['ema20'] > curr_1h['ema50']) or (curr_1h['close'] > curr_1h['ema20'])
256
+ if is_bullish and (45 <= curr_1h['rsi'] <= 75):
257
  vol_ma = df_15m['volume'].rolling(20).mean().iloc[-1]
258
 
259
+ # 🔧 TUNED: 1.2x Volume
260
  if curr_15m['volume'] >= 1.2 * vol_ma:
261
  score = curr_15m['volume'] / vol_ma if vol_ma > 0 else 1.0
262
  return {'type': 'BREAKOUT', 'score': score}
 
271
  # ------------------------------------------------------------------
272
  # Helpers
273
  # ------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  async def _fetch_technical_data_batch(self, candidates):
275
  chunk_size = 10; results = []
276
  for i in range(0, len(candidates), chunk_size):