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

Update ml_engine/data_manager.py

Browse files
Files changed (1) hide show
  1. ml_engine/data_manager.py +36 -75
ml_engine/data_manager.py CHANGED
@@ -1,6 +1,6 @@
1
  # ============================================================
2
  # 📂 ml_engine/data_manager.py
3
- # (V60.0 - GEM-Architect: Anti-FOMO Shield + Neural Injection)
4
  # ============================================================
5
 
6
  import asyncio
@@ -12,13 +12,11 @@ import pandas as pd
12
  import numpy as np
13
  from typing import List, Dict, Any
14
 
15
- # Keep SystemLimits import for fallbacks if needed
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,7 +25,7 @@ class DataManager:
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 # 🧠 المرجع لملف التعلم (Adaptive Hub)
31
 
32
  self.exchange = ccxt.kucoin({
33
  'enableRateLimit': True,
@@ -43,7 +41,7 @@ class DataManager:
43
  'UP', 'DOWN', 'BEAR', 'BULL', '3S', '3L'
44
  ]
45
 
46
- print(f"📦 [DataManager V60.0] Anti-FOMO Shield + Context Injector Active.")
47
 
48
  async def initialize(self):
49
  print(" > [DataManager] Starting initialization...")
@@ -75,150 +73,119 @@ class DataManager:
75
  # 🧠 Layer 1: Screening + Diagnosis + Injection
76
  # ==================================================================
77
  async def layer1_rapid_screening(self, adaptive_hub_ref=None) -> List[Dict[str, Any]]:
78
- """
79
- 1. Filters using Strict Anti-FOMO Logic.
80
- 2. Diagnoses Market Regime (Bull/Bear/Range) for survivors.
81
- 3. Injects Dynamic Thresholds from AdaptiveHub.
82
- """
83
  self.adaptive_hub_ref = adaptive_hub_ref
84
- print(f"🔍 [Layer 1] Initiating STRICT Screening & Context Injection...")
85
 
86
- # 1. فلتر السيولة الأساسي
87
  initial_candidates = await self._stage0_universe_filter()
88
- if not initial_candidates: return []
 
 
 
89
 
90
  # 2. جلب البيانات الفنية
91
- top_candidates = initial_candidates[:100]
92
  enriched_data = await self._fetch_technical_data_batch(top_candidates)
93
 
94
  final_list = []
95
 
96
  for item in enriched_data:
97
- # 3. تطبيق الفلتر الصارم (Anti-FOMO)
98
  classification = self._apply_strict_logic_tree(item)
99
 
100
  if classification['type'] != 'NONE':
101
- # 4. التشخيص والحقن (The Injection Step)
102
- # نحدد حالة العملة (صاعدة/هابطة/ميتة) لتحديد العتبات المناسبة
103
  regime_info = self._diagnose_asset_regime(item)
104
  item['asset_regime'] = regime_info['regime']
105
  item['asset_regime_conf'] = regime_info['conf']
106
 
107
- # ✅ حقن العتبات الخاصة من AdaptiveHub
108
  if self.adaptive_hub_ref:
109
- # يجلب الإعدادات الخاصة بهذه الحالة (مثلاً: BULL يحتاج عتبات أقل)
110
  dynamic_config = self.adaptive_hub_ref.get_regime_config(regime_info['regime'])
111
  item['dynamic_limits'] = dynamic_config
112
 
113
- # حفظ النتيجة
114
  item['l1_sort_score'] = classification['score']
115
  item['strategy_tag'] = classification['type']
116
  final_list.append(item)
117
 
118
  # 5. الترتيب النهائي
119
- # نفضل الاختراقات الآمنة (Breakout) والارتدادات القوية
120
  final_list.sort(key=lambda x: x['l1_sort_score'], reverse=True)
121
 
122
  selection = final_list[:50]
123
- print(f"✅ [Layer 1] Injected Context into {len(selection)} Candidates.")
124
  return selection
125
 
126
  # ------------------------------------------------------------------
127
- # 🧭 The Diagnoser (يحدد حالة العملة لحقن العتبات)
128
  # ------------------------------------------------------------------
129
  def _diagnose_asset_regime(self, item: Dict[str, Any]) -> Dict[str, Any]:
130
- """
131
- تحليل حالة العملة بناءً على البيانات المحسوبة مسبقاً (1H).
132
- يحدد هل هي BULL, BEAR, RANGE, أم DEAD.
133
- """
134
  try:
135
- # نستخدم بيانات الـ 1 ساعة المحسوبة في _apply_strict_logic_tree
136
- # البيانات موجودة في item['df_1h'] (سنقوم بحفظها هناك)
137
  if 'df_1h' not in item: return {'regime': 'RANGE', 'conf': 0.0}
138
-
139
  df = item['df_1h']
140
  curr = df.iloc[-1]
141
-
142
  price = curr['close']
143
  ema20 = curr['ema20']
144
  ema50 = curr['ema50']
145
  rsi = curr['rsi']
146
  atr = curr['atr']
147
-
148
- # حساب نسبة الـ ATR (لقياس النشاط)
149
  atr_pct = (atr / price) * 100 if price > 0 else 0
150
 
151
  regime = "RANGE"
152
  conf = 0.5
153
 
154
- # 1. DEAD Check (خمول تام)
155
- # حركة ضعيفة جداً وسيولة منخفضة
156
- if atr_pct < 0.5:
157
- return {'regime': 'DEAD', 'conf': 0.9}
158
-
159
- # 2. BULL Check (ترند صاعد قوي)
160
- # السعر فوق المتوسطات، والمتوسطات مرتبة، والزخم إيجابي
161
  if price > ema20 and ema20 > ema50 and rsi > 50:
162
  regime = "BULL"
163
  conf = 0.8 if rsi > 55 else 0.6
164
-
165
- # 3. BEAR Check (ترند هابط)
166
  elif price < ema20 and ema20 < ema50 and rsi < 50:
167
  regime = "BEAR"
168
  conf = 0.8 if rsi < 45 else 0.6
169
 
170
  return {'regime': regime, 'conf': conf}
171
-
172
- except Exception:
173
- return {'regime': 'RANGE', 'conf': 0.0}
174
 
175
  # ------------------------------------------------------------------
176
- # 🛡️ The Strict Logic Tree (Anti-FOMO)
177
  # ------------------------------------------------------------------
178
  def _apply_strict_logic_tree(self, data: Dict[str, Any]) -> Dict[str, Any]:
179
  try:
180
- # حساب المؤشرات
181
  df_1h = self._calc_indicators(data['ohlcv_1h_raw'])
182
  df_15m = self._calc_indicators(data['ohlcv_15m_raw'])
183
-
184
- # حفظ الداتافريم للتشخيص لاحقاً
185
  data['df_1h'] = df_1h
186
-
187
  except: return {'type': 'NONE', 'score': 0}
188
 
189
  curr_1h = df_1h.iloc[-1]
190
  curr_15m = df_15m.iloc[-1]
191
 
192
- # --- فلاتر الأمان (Safety Gates) ---
193
  try:
194
  close_4h_ago = df_1h.iloc[-5]['close']
195
  change_4h = ((curr_1h['close'] - close_4h_ago) / close_4h_ago) * 100
196
  except: change_4h = 0.0
197
 
198
- # 1. لا تطارد المضخات (+8% في 4 ساعات ممنوع)
199
- if change_4h > 8.0: return {'type': 'NONE', 'score': 0}
200
- # 2. لا تشتري في قمة التشبع (RSI > 70 ممنوع)
201
- if curr_1h['rsi'] > 70: return {'type': 'NONE', 'score': 0}
202
- # 3. لا تشتري بعيداً عن المتوسط (Mean Reversion Risk)
 
 
 
203
  dev = (curr_1h['close'] - curr_1h['ema20']) / curr_1h['atr'] if curr_1h['atr'] > 0 else 0
204
- if dev > 1.8: return {'type': 'NONE', 'score': 0}
205
 
206
  # --- التصنيف ---
207
 
208
  # A. Breakout (اختراق آمن)
209
- # هيكلية صاعدة + تجميع (Squeeze) + فوليوم
210
  is_bullish = (curr_1h['ema20'] > curr_1h['ema50']) or (curr_1h['close'] > curr_1h['ema20'])
211
- if is_bullish and (45 <= curr_1h['rsi'] <= 68):
212
  vol_ma = df_15m['volume'].rolling(20).mean().iloc[-1]
213
- if curr_15m['volume'] >= 1.5 * vol_ma: # شرط الفوليوم
214
- # سكور الاختراق يعتمد على قوة الفوليوم النسبي
 
215
  score = curr_15m['volume'] / vol_ma if vol_ma > 0 else 1.0
216
  return {'type': 'BREAKOUT', 'score': score}
217
 
218
  # B. Reversal (صيد القاع)
219
- # تشبع بيعي + شمعة عاكسة
220
  if 20 <= curr_1h['rsi'] <= 40 and change_4h <= -2.0:
221
- # سكور الارتداد يعتمد على مدى انخفاض الـ RSI (كلما قل كان أفضل للارتداد)
222
  score = (100 - curr_1h['rsi'])
223
  return {'type': 'REVERSAL', 'score': score}
224
 
@@ -235,12 +202,12 @@ class DataManager:
235
  if not symbol.endswith('/USDT'): continue
236
  if any(b in symbol for b in self.BLACKLIST_TOKENS): continue
237
 
238
- # السيولة
239
  quote_vol = ticker.get('quoteVolume', 0)
240
- if quote_vol < 1_000_000: continue
241
 
242
- # حماية من العملات المنفجرة (+15% يومي ممنوع)
243
- if ticker.get('percentage', 0) > 15.0: continue
244
 
245
  candidates.append({
246
  'symbol': symbol,
@@ -264,32 +231,26 @@ class DataManager:
264
 
265
  async def _fetch_single(self, c):
266
  try:
267
- # نحتاج 1H و 15M للتشخيص والفلتر
268
  h1 = await self.exchange.fetch_ohlcv(c['symbol'], '1h', limit=60)
269
  m15 = await self.exchange.fetch_ohlcv(c['symbol'], '15m', limit=60)
270
  if not h1 or not m15: return None
271
- c['ohlcv'] = {'1h': h1, '15m': m15} # للمعالج لاحقاً
272
- c['ohlcv_1h_raw'] = h1 # للفلتر الداخلي
273
  c['ohlcv_15m_raw'] = m15
274
  return c
275
  except: return None
276
 
277
  def _calc_indicators(self, ohlcv):
278
  df = pd.DataFrame(ohlcv, columns=['ts', 'o', 'h', 'l', 'c', 'v'])
279
- # RSI
280
  delta = df['c'].diff()
281
  gain = (delta.where(delta>0, 0)).rolling(14).mean()
282
  loss = (-delta.where(delta<0, 0)).rolling(14).mean()
283
  rs = gain/loss
284
  df['rsi'] = 100 - (100/(1+rs))
285
- # EMAs
286
  df['ema20'] = df['c'].ewm(span=20).mean()
287
  df['ema50'] = df['c'].ewm(span=50).mean()
288
- # ATR
289
  tr = np.maximum(df['h']-df['l'], np.maximum(abs(df['h']-df['c'].shift()), abs(df['l']-df['c'].shift())))
290
  df['atr'] = tr.rolling(14).mean()
291
-
292
- # Renaming for consistency
293
  df.rename(columns={'o':'open', 'h':'high', 'l':'low', 'c':'close', 'v':'volume'}, inplace=True)
294
  return df.fillna(0)
295
 
 
1
  # ============================================================
2
  # 📂 ml_engine/data_manager.py
3
+ # (V60.1 - GEM-Architect: Tuned Anti-FOMO Shield)
4
  # ============================================================
5
 
6
  import asyncio
 
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
  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,
 
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...")
 
73
  # 🧠 Layer 1: Screening + Diagnosis + Injection
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':
97
+ # 4. التشخيص والحقن
 
98
  regime_info = self._diagnose_asset_regime(item)
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
105
 
 
106
  item['l1_sort_score'] = classification['score']
107
  item['strategy_tag'] = classification['type']
108
  final_list.append(item)
109
 
110
  # 5. الترتيب النهائي
 
111
  final_list.sort(key=lambda x: x['l1_sort_score'], reverse=True)
112
 
113
  selection = final_list[:50]
114
+ print(f"✅ [Layer 1] Passed {len(selection)} candidates to Processor.")
115
  return selection
116
 
117
  # ------------------------------------------------------------------
118
+ # 🧭 The Diagnoser
119
  # ------------------------------------------------------------------
120
  def _diagnose_asset_regime(self, item: Dict[str, Any]) -> Dict[str, Any]:
 
 
 
 
121
  try:
 
 
122
  if 'df_1h' not in item: return {'regime': 'RANGE', 'conf': 0.0}
 
123
  df = item['df_1h']
124
  curr = df.iloc[-1]
 
125
  price = curr['close']
126
  ema20 = curr['ema20']
127
  ema50 = curr['ema50']
128
  rsi = curr['rsi']
129
  atr = curr['atr']
 
 
130
  atr_pct = (atr / price) * 100 if price > 0 else 0
131
 
132
  regime = "RANGE"
133
  conf = 0.5
134
 
135
+ if atr_pct < 0.5: return {'regime': 'DEAD', 'conf': 0.9}
 
 
 
 
 
 
136
  if price > ema20 and ema20 > ema50 and rsi > 50:
137
  regime = "BULL"
138
  conf = 0.8 if rsi > 55 else 0.6
 
 
139
  elif price < ema20 and ema20 < ema50 and rsi < 50:
140
  regime = "BEAR"
141
  conf = 0.8 if rsi < 45 else 0.6
142
 
143
  return {'regime': regime, 'conf': conf}
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:
 
151
  df_1h = self._calc_indicators(data['ohlcv_1h_raw'])
152
  df_15m = self._calc_indicators(data['ohlcv_15m_raw'])
 
 
153
  data['df_1h'] = df_1h
 
154
  except: return {'type': 'NONE', 'score': 0}
155
 
156
  curr_1h = df_1h.iloc[-1]
157
  curr_15m = df_15m.iloc[-1]
158
 
 
159
  try:
160
  close_4h_ago = df_1h.iloc[-5]['close']
161
  change_4h = ((curr_1h['close'] - close_4h_ago) / close_4h_ago) * 100
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
 
175
  # --- التصنيف ---
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}
186
 
187
  # B. Reversal (صيد القاع)
 
188
  if 20 <= curr_1h['rsi'] <= 40 and change_4h <= -2.0:
 
189
  score = (100 - curr_1h['rsi'])
190
  return {'type': 'REVERSAL', 'score': score}
191
 
 
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,
 
231
 
232
  async def _fetch_single(self, c):
233
  try:
 
234
  h1 = await self.exchange.fetch_ohlcv(c['symbol'], '1h', limit=60)
235
  m15 = await self.exchange.fetch_ohlcv(c['symbol'], '15m', limit=60)
236
  if not h1 or not m15: return None
237
+ c['ohlcv'] = {'1h': h1, '15m': m15}
238
+ c['ohlcv_1h_raw'] = h1
239
  c['ohlcv_15m_raw'] = m15
240
  return c
241
  except: return None
242
 
243
  def _calc_indicators(self, ohlcv):
244
  df = pd.DataFrame(ohlcv, columns=['ts', 'o', 'h', 'l', 'c', 'v'])
 
245
  delta = df['c'].diff()
246
  gain = (delta.where(delta>0, 0)).rolling(14).mean()
247
  loss = (-delta.where(delta<0, 0)).rolling(14).mean()
248
  rs = gain/loss
249
  df['rsi'] = 100 - (100/(1+rs))
 
250
  df['ema20'] = df['c'].ewm(span=20).mean()
251
  df['ema50'] = df['c'].ewm(span=50).mean()
 
252
  tr = np.maximum(df['h']-df['l'], np.maximum(abs(df['h']-df['c'].shift()), abs(df['l']-df['c'].shift())))
253
  df['atr'] = tr.rolling(14).mean()
 
 
254
  df.rename(columns={'o':'open', 'h':'high', 'l':'low', 'c':'close', 'v':'volume'}, inplace=True)
255
  return df.fillna(0)
256