Riy777 commited on
Commit
feaec85
·
verified ·
1 Parent(s): a9383fb

Update ml_engine/processor.py

Browse files
Files changed (1) hide show
  1. ml_engine/processor.py +39 -127
ml_engine/processor.py CHANGED
@@ -1,9 +1,7 @@
1
  # ml_engine/processor.py
2
- # (V13.11 - GEM-Architect: Full Enterprise Logic + Sniper Entry Fix)
3
- # - Features: Feature Engineering, Titan Score, Pattern Score.
4
- # - Stats: Hybrid Monte Carlo (Light + Advanced).
5
- # - Decisions: Guardian (Open Trades), Oracle (Filtering).
6
- # - Execution: Sniper Entry Check (Micro-structure analysis) [ADDED].
7
 
8
  import asyncio
9
  import numpy as np
@@ -33,116 +31,77 @@ class MLProcessor:
33
 
34
  def __init__(self, data_manager):
35
  self.data_manager = data_manager
36
- self.hub_manager = None # Dependency Injection later
37
-
38
- # حالة التهيئة
39
  self.initialized = False
40
-
41
- # تهيئة محرك المحاكاة
42
  self.mc_engine = MonteCarloEngine()
43
 
44
- # مخزن النماذج
45
  self.models = {
46
  'titan_xgb': None,
47
  'pattern_recognition': None
48
  }
49
 
50
- # الأوزان الأساسية للتحليل الأولي
51
  self.weights = {
52
  'titan': 0.50,
53
  'pattern': 0.30,
54
- 'light_mc': 1.0 # Bonus added directly
55
  }
56
 
57
- # إعدادات العتبات (Decision Thresholds)
58
  self.thresholds = {
59
  'buy_moderate': 0.62,
60
  'stop_loss_hard': -0.05,
61
  'take_profit_base': 0.025
62
  }
63
 
64
- print("✅ [MLProcessor V13.11] Enterprise Engine Loaded (Sniper Ready).")
65
 
66
  async def initialize(self):
67
- """
68
- تهيئة النظام وتحميل الموارد الثقيلة.
69
- """
70
  print("🔄 [Processor] Initializing Neural Core...")
71
  try:
72
- # Placeholder for model loading logic
73
- # await self._load_models_from_disk()
74
-
75
  print(" -> Analytics Engines: Online")
76
  print(" -> Monte Carlo: Hybrid Mode Active")
77
-
78
  self.initialized = True
79
  print("✅ [MLProcessor] Initialization Complete.")
80
-
81
  except Exception as e:
82
  print(f"❌ [Processor] Init Error: {e}")
83
  self.initialized = False
84
 
85
  # ==============================================================================
86
- # 🧠 Layer 2: Primary Signal Processing (Batch Analysis)
87
  # ==============================================================================
88
 
89
  async def process_compound_signal(self, raw_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
90
- """
91
- المعالج الرئيسي للـ 150 عملة.
92
- يطبق التحليل الفني + الأنماط + Light MC.
93
- """
94
  symbol = raw_data.get('symbol', 'UNKNOWN')
95
-
96
  try:
97
- # 1. التحقق من البيانات
98
- if not self._validate_data(raw_data):
99
- return None
100
-
101
- # 2. استخراج الميزات
102
  features = self._extract_features(raw_data)
103
- if not features:
104
- return None
105
 
106
- # 3. التحليل الفني (Titan Score)
107
  titan_score = self._calculate_titan_score(features)
108
-
109
- # 4. تحليل الأنماط (Pattern Score)
110
  pattern_score = self._calculate_pattern_score(features)
111
 
112
- # 5. تحليل مونت كارلو الخفيف (Light MC)
113
- # يستخدم إغلاقات 15 دقيقة للسرعة
114
  prices_15m = features.get('closes_15m', [])
115
  mc_light_bonus = 0.0
116
  if len(prices_15m) > 30:
117
  mc_light_bonus = self.mc_engine.run_light_check(prices_15m)
118
 
119
- # 6. حساب الدرجة الأساسية
120
  base_score = (titan_score * self.weights['titan']) + \
121
  (pattern_score * self.weights['pattern']) + \
122
  mc_light_bonus
123
 
124
  final_score = round(max(0.0, base_score), 4)
125
 
126
- # 7. بناء النتيجة
127
  result_package = {
128
  'symbol': symbol,
129
  'current_price': raw_data.get('current_price', 0.0),
130
  'timestamp': raw_data.get('timestamp', time.time()),
131
-
132
- # الدرجة النهائية
133
  'enhanced_final_score': final_score,
134
-
135
- # [تفاصيل الجدول]
136
  'titan_score': round(titan_score, 4),
137
  'pattern_score': round(pattern_score, 4),
138
- 'monte_carlo_score': round(mc_light_bonus, 2), # Light MC Result
139
-
140
  'signal_type': 'BUY' if final_score > 0.5 else 'HOLD',
141
  'risk_level': self._calculate_risk_level(features)
142
  }
143
-
144
  return result_package
145
-
146
  except Exception as e:
147
  return None
148
 
@@ -151,11 +110,9 @@ class MLProcessor:
151
  # ==============================================================================
152
 
153
  async def run_advanced_monte_carlo(self, symbol: str, timeframe: str = '1h') -> float:
154
- """تشغيل المحاكاة المتقدمة لأفضل المرشحين فقط (L2.5)."""
155
  try:
156
  ohlcv = await self.data_manager.get_latest_ohlcv(symbol, timeframe, limit=500)
157
- if not ohlcv or len(ohlcv) < 100:
158
- return 0.0
159
  prices = [c[4] for c in ohlcv]
160
  adv_score = self.mc_engine.run_advanced_simulation(prices, num_simulations=3000, time_horizon=24)
161
  return adv_score
@@ -163,125 +120,96 @@ class MLProcessor:
163
  print(f"⚠️ [Processor] Advanced MC Error ({symbol}): {e}")
164
  return 0.0
165
 
166
- async def check_sniper_entry(self, ohlcv_1m: List[list], order_book: Dict) -> bool:
167
  """
168
- [L4 SNIPER LOGIC] - الدالة المضافة
169
- فحص دقيق قبل التنفيذ مباشرة. تتأكد من:
170
- 1. السبريد (Spread) مقبول.
171
- 2. لا توجد جدران بيع ضخمة (Sell Walls).
172
- 3. الزخم اللحظي (1m Momentum) ليس سلبياً بحدة.
173
  """
174
  try:
175
- # 1. فحص دفتر الطلبات (Order Book)
176
  bids = order_book.get('bids', [])
177
  asks = order_book.get('asks', [])
178
 
179
  if not bids or not asks:
180
- # إذا لم تتوفر البيانات، نسمح بالدخول (Fail Open) لتجنب تفويت الفرص بسبب نقص البيانات اللحظي
181
- return True
182
 
183
  best_bid = float(bids[0][0])
184
  best_ask = float(asks[0][0])
185
 
186
- # حساب السبريد
187
  spread_pct = (best_ask - best_bid) / best_bid
188
- if spread_pct > 0.01: # 1% Spread is too high for scalping
189
- print(f" ⚠️ [Sniper] High Spread: {spread_pct*100:.2f}%")
190
- return False
191
 
192
- # فحص جدار البيع (Sell Wall)
193
- # نجمع حجم أول 5 طلبات
194
  bid_vol = sum([b[1] for b in bids[:5]])
195
  ask_vol = sum([a[1] for a in asks[:5]])
196
 
197
- if ask_vol > bid_vol * 4: # ضغط بيع هائل في الواجهة
198
- print(f" ⚠️ [Sniper] Sell Wall Detected (Ratio 1:{ask_vol/bid_vol:.1f})")
199
- return False
200
 
201
- # 2. فحص الزخم اللحظي (1m Candles)
202
  if ohlcv_1m and len(ohlcv_1m) >= 3:
203
  closes = [c[4] for c in ohlcv_1m]
204
- # إذا كانت آخر شمعتين هبوط حاد، ننتظر قليلاً
205
- # (بسيط جداً حالياً، يمكن تعقيده)
206
  change_last_2m = (closes[-1] - closes[-3]) / closes[-3]
207
- if change_last_2m < -0.015: # هبوط 1.5% في دقيقتين
208
- print(" ⚠️ [Sniper] Falling Knife detected (-1.5% in 2m)")
209
- return False
210
 
211
- return True # Entry Approved
212
 
213
  except Exception as e:
214
- print(f"⚠️ [Sniper] Check Error: {e}. Proceeding cautiously.")
215
- return True # السماح بالدخول في حالة الخطأ (Fail Open)
 
 
216
 
217
  # ==============================================================================
218
- # ⚙️ Internal Logic: Indicators & Features
219
  # ==============================================================================
220
 
221
  def _validate_data(self, raw_data: Dict) -> bool:
222
  if not raw_data: return False
223
  if 'ohlcv' not in raw_data: return False
224
  if '15m' not in raw_data['ohlcv']: return False
225
- if not raw_data['ohlcv']['15m']: return False
226
  return True
227
 
228
  def _extract_features(self, raw_data: Dict) -> Dict:
229
  try:
230
  candles = raw_data['ohlcv'].get('15m', [])
231
  if not candles: return {}
232
-
233
  closes = np.array([c[4] for c in candles], dtype=float)
234
  volumes = np.array([c[5] for c in candles], dtype=float)
235
-
236
- return {
237
- 'closes_15m': closes,
238
- 'volumes_15m': volumes,
239
- 'last_close': closes[-1],
240
- 'last_vol': volumes[-1]
241
- }
242
  except: return {}
243
 
244
  def _calculate_titan_score(self, features: Dict) -> float:
245
- """Titan Logic: RSI + Trend"""
246
  try:
247
  closes = features.get('closes_15m')
248
  if closes is None or len(closes) < 14: return 0.5
249
-
250
- # RSI Calculation
251
  deltas = np.diff(closes)
252
  seed = deltas[:14+1]
253
  up = seed[seed >= 0].sum()/14
254
  down = -seed[seed < 0].sum()/14
255
  rs = up/down if down != 0 else 0
256
  rsi = 100 - (100 / (1 + rs))
257
-
258
  score = 0.5
259
-
260
- # Logic: Buy dips in uptrends
261
  if 30 < rsi < 70: score = 0.6
262
- elif rsi <= 30: score = 0.8 # Oversold
263
- elif rsi >= 75: score = 0.3 # Overbought
264
-
265
- # Trend Confirmation
266
  sma_20 = np.mean(closes[-20:]) if len(closes) >= 20 else np.mean(closes)
267
  if closes[-1] > sma_20: score += 0.1
268
-
269
  return max(0.0, min(1.0, score))
270
  except: return 0.5
271
 
272
  def _calculate_pattern_score(self, features: Dict) -> float:
273
- """Pattern Logic: Volume"""
274
  try:
275
  volumes = features.get('volumes_15m')
276
  if volumes is None or len(volumes) < 20: return 0.5
277
-
278
  avg_vol = np.mean(volumes[:-5])
279
  curr_vol = np.mean(volumes[-3:])
280
-
281
  score = 0.5
282
  if curr_vol > avg_vol * 2.5: score = 0.9
283
  elif curr_vol > avg_vol * 1.5: score = 0.7
284
-
285
  return max(0.0, min(1.0, score))
286
  except: return 0.5
287
 
@@ -289,44 +217,32 @@ class MLProcessor:
289
  try:
290
  closes = features.get('closes_15m')
291
  if closes is None: return "Unknown"
292
- log_returns = np.diff(np.log(closes))
293
- volatility = np.std(log_returns)
294
  if volatility > 0.02: return "High"
295
  if volatility > 0.01: return "Medium"
296
  return "Low"
297
  except: return "Unknown"
298
 
299
  # ==============================================================================
300
- # 🛡️ Decision Layers: Guardian & Oracle
301
  # ==============================================================================
302
 
303
  def consult_guardian(self, d1, d5, d15, entry_price):
304
- """Guardian Logic for Open Trades."""
305
  try:
306
- if not d1 or len(d1) == 0:
307
- return {'action': 'HOLD', 'reason': 'No Data'}
308
-
309
  current_price = d1[-1][4]
310
  if entry_price == 0: return {'action': 'HOLD', 'reason': 'Zero Entry'}
311
-
312
  pnl_pct = (current_price - entry_price) / entry_price
313
-
314
- # Stop Loss
315
  if pnl_pct < self.thresholds['stop_loss_hard']:
316
  return {'action': 'EXIT_HARD', 'reason': f'Stop Loss ({pnl_pct*100:.2f}%)'}
317
-
318
- # Take Profit
319
  if pnl_pct > 0.05:
320
- return {'action': 'EXIT_PARTIAL', 'reason': 'Secure Profit (>5%)'}
321
-
322
  return {'action': 'HOLD', 'reason': 'Neutral'}
323
-
324
  except Exception as e:
325
  print(f"⚠️ [Guardian] Error: {e}")
326
  return {'action': 'HOLD', 'reason': 'Guardian Error'}
327
 
328
  async def consult_oracle(self, signal):
329
- """Oracle Logic for New Signals."""
330
  try:
331
  symbol = signal.get('symbol', 'UNKNOWN')
332
  conf = signal.get('enhanced_final_score', 0.0)
@@ -337,7 +253,6 @@ class MLProcessor:
337
  if conf >= threshold:
338
  tp = price * 1.03
339
  sl = price * 0.975
340
-
341
  print(f" 🔮 [Oracle] APPROVED {symbol}: Score {conf:.2f} >= {threshold}")
342
  return {
343
  'action': 'WATCH',
@@ -351,12 +266,9 @@ class MLProcessor:
351
  return {'action': 'IGNORE', 'reason': f'Score {conf:.2f} < {threshold}'}
352
 
353
  except Exception as e:
354
- print(f"⚠️ [Oracle] Critical Error: {e}")
355
  return {'action': 'IGNORE', 'reason': 'Oracle Logic Error'}
356
 
357
- # ==============================================================================
358
- # 🧹 Cleanup
359
- # ==============================================================================
360
  async def cleanup(self):
361
  self.models.clear()
362
  gc.collect()
 
1
  # ml_engine/processor.py
2
+ # (V13.12 - GEM-Architect: Sniper Return Type Fixed)
3
+ # - Fixed check_sniper_entry to return Dict {'passed': bool, 'reason': str}
4
+ # - Prevents AttributeError in TradeManager.
 
 
5
 
6
  import asyncio
7
  import numpy as np
 
31
 
32
  def __init__(self, data_manager):
33
  self.data_manager = data_manager
34
+ self.hub_manager = None
 
 
35
  self.initialized = False
 
 
36
  self.mc_engine = MonteCarloEngine()
37
 
 
38
  self.models = {
39
  'titan_xgb': None,
40
  'pattern_recognition': None
41
  }
42
 
 
43
  self.weights = {
44
  'titan': 0.50,
45
  'pattern': 0.30,
46
+ 'light_mc': 1.0
47
  }
48
 
 
49
  self.thresholds = {
50
  'buy_moderate': 0.62,
51
  'stop_loss_hard': -0.05,
52
  'take_profit_base': 0.025
53
  }
54
 
55
+ print("✅ [MLProcessor V13.12] Enterprise Engine Loaded (Sniper Dict Fix).")
56
 
57
  async def initialize(self):
 
 
 
58
  print("🔄 [Processor] Initializing Neural Core...")
59
  try:
 
 
 
60
  print(" -> Analytics Engines: Online")
61
  print(" -> Monte Carlo: Hybrid Mode Active")
 
62
  self.initialized = True
63
  print("✅ [MLProcessor] Initialization Complete.")
 
64
  except Exception as e:
65
  print(f"❌ [Processor] Init Error: {e}")
66
  self.initialized = False
67
 
68
  # ==============================================================================
69
+ # 🧠 Layer 2: Primary Signal Processing
70
  # ==============================================================================
71
 
72
  async def process_compound_signal(self, raw_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
 
 
 
 
73
  symbol = raw_data.get('symbol', 'UNKNOWN')
 
74
  try:
75
+ if not self._validate_data(raw_data): return None
 
 
 
 
76
  features = self._extract_features(raw_data)
77
+ if not features: return None
 
78
 
 
79
  titan_score = self._calculate_titan_score(features)
 
 
80
  pattern_score = self._calculate_pattern_score(features)
81
 
 
 
82
  prices_15m = features.get('closes_15m', [])
83
  mc_light_bonus = 0.0
84
  if len(prices_15m) > 30:
85
  mc_light_bonus = self.mc_engine.run_light_check(prices_15m)
86
 
 
87
  base_score = (titan_score * self.weights['titan']) + \
88
  (pattern_score * self.weights['pattern']) + \
89
  mc_light_bonus
90
 
91
  final_score = round(max(0.0, base_score), 4)
92
 
 
93
  result_package = {
94
  'symbol': symbol,
95
  'current_price': raw_data.get('current_price', 0.0),
96
  'timestamp': raw_data.get('timestamp', time.time()),
 
 
97
  'enhanced_final_score': final_score,
 
 
98
  'titan_score': round(titan_score, 4),
99
  'pattern_score': round(pattern_score, 4),
100
+ 'monte_carlo_score': round(mc_light_bonus, 2),
 
101
  'signal_type': 'BUY' if final_score > 0.5 else 'HOLD',
102
  'risk_level': self._calculate_risk_level(features)
103
  }
 
104
  return result_package
 
105
  except Exception as e:
106
  return None
107
 
 
110
  # ==============================================================================
111
 
112
  async def run_advanced_monte_carlo(self, symbol: str, timeframe: str = '1h') -> float:
 
113
  try:
114
  ohlcv = await self.data_manager.get_latest_ohlcv(symbol, timeframe, limit=500)
115
+ if not ohlcv or len(ohlcv) < 100: return 0.0
 
116
  prices = [c[4] for c in ohlcv]
117
  adv_score = self.mc_engine.run_advanced_simulation(prices, num_simulations=3000, time_horizon=24)
118
  return adv_score
 
120
  print(f"⚠️ [Processor] Advanced MC Error ({symbol}): {e}")
121
  return 0.0
122
 
123
+ async def check_sniper_entry(self, ohlcv_1m: List[list], order_book: Dict) -> Dict[str, Any]:
124
  """
125
+ [L4 SNIPER LOGIC] - [FIXED RETURN TYPE]
126
+ Must return a Dictionary {'passed': bool, 'reason': str}
 
 
 
127
  """
128
  try:
129
+ # 1. Order Book Analysis
130
  bids = order_book.get('bids', [])
131
  asks = order_book.get('asks', [])
132
 
133
  if not bids or not asks:
134
+ return {'passed': True, 'reason': 'OB Data Unavailable (Fail Open)'}
 
135
 
136
  best_bid = float(bids[0][0])
137
  best_ask = float(asks[0][0])
138
 
139
+ # Spread Check
140
  spread_pct = (best_ask - best_bid) / best_bid
141
+ if spread_pct > 0.015: # 1.5% Max Spread
142
+ return {'passed': False, 'reason': f'High Spread ({spread_pct*100:.2f}%)'}
 
143
 
144
+ # Sell Wall Check
 
145
  bid_vol = sum([b[1] for b in bids[:5]])
146
  ask_vol = sum([a[1] for a in asks[:5]])
147
 
148
+ if ask_vol > bid_vol * 5:
149
+ return {'passed': False, 'reason': f'Sell Wall (Ratio 1:{ask_vol/bid_vol:.1f})'}
 
150
 
151
+ # 2. Momentum Check (Falling Knife)
152
  if ohlcv_1m and len(ohlcv_1m) >= 3:
153
  closes = [c[4] for c in ohlcv_1m]
 
 
154
  change_last_2m = (closes[-1] - closes[-3]) / closes[-3]
155
+ if change_last_2m < -0.02: # -2% in 2 mins
156
+ return {'passed': False, 'reason': 'Falling Knife Detected'}
 
157
 
158
+ return {'passed': True, 'reason': 'Sniper Conditions Met'}
159
 
160
  except Exception as e:
161
+ print(f"⚠️ [Sniper] Check Error: {e}")
162
+ # In case of error, we usually default to True to avoid freezing, or False for safety.
163
+ # Choosing True (Fail Open) for continuity.
164
+ return {'passed': True, 'reason': f'Error ({e}) - Fail Open'}
165
 
166
  # ==============================================================================
167
+ # ⚙️ Internal Logic
168
  # ==============================================================================
169
 
170
  def _validate_data(self, raw_data: Dict) -> bool:
171
  if not raw_data: return False
172
  if 'ohlcv' not in raw_data: return False
173
  if '15m' not in raw_data['ohlcv']: return False
 
174
  return True
175
 
176
  def _extract_features(self, raw_data: Dict) -> Dict:
177
  try:
178
  candles = raw_data['ohlcv'].get('15m', [])
179
  if not candles: return {}
 
180
  closes = np.array([c[4] for c in candles], dtype=float)
181
  volumes = np.array([c[5] for c in candles], dtype=float)
182
+ return {'closes_15m': closes, 'volumes_15m': volumes}
 
 
 
 
 
 
183
  except: return {}
184
 
185
  def _calculate_titan_score(self, features: Dict) -> float:
 
186
  try:
187
  closes = features.get('closes_15m')
188
  if closes is None or len(closes) < 14: return 0.5
 
 
189
  deltas = np.diff(closes)
190
  seed = deltas[:14+1]
191
  up = seed[seed >= 0].sum()/14
192
  down = -seed[seed < 0].sum()/14
193
  rs = up/down if down != 0 else 0
194
  rsi = 100 - (100 / (1 + rs))
 
195
  score = 0.5
 
 
196
  if 30 < rsi < 70: score = 0.6
197
+ elif rsi <= 30: score = 0.8
198
+ elif rsi >= 75: score = 0.3
 
 
199
  sma_20 = np.mean(closes[-20:]) if len(closes) >= 20 else np.mean(closes)
200
  if closes[-1] > sma_20: score += 0.1
 
201
  return max(0.0, min(1.0, score))
202
  except: return 0.5
203
 
204
  def _calculate_pattern_score(self, features: Dict) -> float:
 
205
  try:
206
  volumes = features.get('volumes_15m')
207
  if volumes is None or len(volumes) < 20: return 0.5
 
208
  avg_vol = np.mean(volumes[:-5])
209
  curr_vol = np.mean(volumes[-3:])
 
210
  score = 0.5
211
  if curr_vol > avg_vol * 2.5: score = 0.9
212
  elif curr_vol > avg_vol * 1.5: score = 0.7
 
213
  return max(0.0, min(1.0, score))
214
  except: return 0.5
215
 
 
217
  try:
218
  closes = features.get('closes_15m')
219
  if closes is None: return "Unknown"
220
+ volatility = np.std(np.diff(np.log(closes)))
 
221
  if volatility > 0.02: return "High"
222
  if volatility > 0.01: return "Medium"
223
  return "Low"
224
  except: return "Unknown"
225
 
226
  # ==============================================================================
227
+ # 🛡️ Guardian & Oracle
228
  # ==============================================================================
229
 
230
  def consult_guardian(self, d1, d5, d15, entry_price):
 
231
  try:
232
+ if not d1 or len(d1) == 0: return {'action': 'HOLD', 'reason': 'No Data'}
 
 
233
  current_price = d1[-1][4]
234
  if entry_price == 0: return {'action': 'HOLD', 'reason': 'Zero Entry'}
 
235
  pnl_pct = (current_price - entry_price) / entry_price
 
 
236
  if pnl_pct < self.thresholds['stop_loss_hard']:
237
  return {'action': 'EXIT_HARD', 'reason': f'Stop Loss ({pnl_pct*100:.2f}%)'}
 
 
238
  if pnl_pct > 0.05:
239
+ return {'action': 'EXIT_PARTIAL', 'reason': 'Secure Profit'}
 
240
  return {'action': 'HOLD', 'reason': 'Neutral'}
 
241
  except Exception as e:
242
  print(f"⚠️ [Guardian] Error: {e}")
243
  return {'action': 'HOLD', 'reason': 'Guardian Error'}
244
 
245
  async def consult_oracle(self, signal):
 
246
  try:
247
  symbol = signal.get('symbol', 'UNKNOWN')
248
  conf = signal.get('enhanced_final_score', 0.0)
 
253
  if conf >= threshold:
254
  tp = price * 1.03
255
  sl = price * 0.975
 
256
  print(f" 🔮 [Oracle] APPROVED {symbol}: Score {conf:.2f} >= {threshold}")
257
  return {
258
  'action': 'WATCH',
 
266
  return {'action': 'IGNORE', 'reason': f'Score {conf:.2f} < {threshold}'}
267
 
268
  except Exception as e:
269
+ print(f"⚠️ [Oracle] Error: {e}")
270
  return {'action': 'IGNORE', 'reason': 'Oracle Logic Error'}
271
 
 
 
 
272
  async def cleanup(self):
273
  self.models.clear()
274
  gc.collect()