Riy777 commited on
Commit
d1d1a25
·
verified ·
1 Parent(s): 58e057f

Update ml_engine/processor.py

Browse files
Files changed (1) hide show
  1. ml_engine/processor.py +298 -514
ml_engine/processor.py CHANGED
@@ -1,563 +1,347 @@
1
- # ============================================================
2
- # 🧠 ml_engine/processor.py
3
- # (V68.1 - GEM-Architect: Realistic Oracle Fallback)
4
- # ============================================================
 
 
 
 
5
 
6
  import asyncio
7
  import traceback
8
- import logging
9
- import os
10
- import sys
11
  import numpy as np
 
 
12
  from typing import Dict, Any, List, Optional
13
 
14
- # --- استيراد المحركات (كما هي) ---
15
- try: from .titan_engine import TitanEngine
16
- except ImportError: TitanEngine = None
17
- try: from .patterns import ChartPatternAnalyzer
18
- except ImportError: ChartPatternAnalyzer = None
19
- try: from .monte_carlo import MonteCarloEngine
20
- except ImportError: MonteCarloEngine = None
21
- try: from .oracle_engine import OracleEngine
22
- except ImportError: OracleEngine = None
23
- try: from .sniper_engine import SniperEngine
24
- except ImportError: SniperEngine = None
25
- try: from .hybrid_guardian import HybridDeepSteward
26
- except ImportError: HybridDeepSteward = None
27
- try: from .guardian_hydra import GuardianHydra
28
- except ImportError: GuardianHydra = None
29
-
30
- # ============================================================
31
- # 📂 مسارات النماذج
32
- # ============================================================
33
- BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
34
- MODELS_L2_DIR = os.path.join(BASE_DIR, "ml_models", "layer2")
35
- MODELS_PATTERN_DIR = os.path.join(BASE_DIR, "ml_models", "xgboost_pattern2")
36
- MODELS_UNIFIED_DIR = os.path.join(BASE_DIR, "ml_models", "Unified_Models_V1")
37
- MODELS_SNIPER_DIR = os.path.join(BASE_DIR, "ml_models", "guard_v2")
38
- MODELS_HYDRA_DIR = os.path.join(BASE_DIR, "ml_models", "guard_v1")
39
- MODEL_V2_PATH = os.path.join(BASE_DIR, "ml_models", "DeepSteward_V2_Production.json")
40
- MODEL_V3_PATH = os.path.join(BASE_DIR, "ml_models", "DeepSteward_V3_Production.json")
41
- MODEL_V3_FEAT = os.path.join(BASE_DIR, "ml_models", "DeepSteward_V3_Features.json")
42
-
43
- # ============================================================
44
- # 🎛️ SYSTEM LIMITS (Realistic Defaults)
45
- # ============================================================
46
  class SystemLimits:
47
- """
48
- GEM-Architect: Adjusted Defaults based on real-world calibration.
49
- Oracle Ceiling detected at ~0.75, so Threshold set to 0.60.
50
- """
51
 
52
- # --- Layer 1 ---
53
- L1_MIN_AFFINITY_SCORE = 15.0
54
 
55
- # --- Layer 2 Hard Gates (Loosened) ---
56
- L2_GATE_TITAN = 0.60
57
- L2_GATE_PATTERN = 0.50
58
- L2_GATE_MC = 0.50
59
-
60
- # --- Layer 2 Weights & Min Score ---
61
- L2_MIN_SCORE = 0.65
62
- L2_WEIGHT_TITAN = 0.40
63
- L2_WEIGHT_PATTERNS = 0.40
64
- L2_WEIGHT_MC = 0.20
65
-
66
- # Pattern Config
67
- PATTERN_TF_WEIGHTS = {'1h': 0.35, '15m': 0.25, '1d': 0.20, '5m': 0.10, '4h': 0.10}
68
- PATTERN_THRESH_BULLISH = 0.50
69
- PATTERN_THRESH_BEARISH = 0.40
70
-
71
- # --- Layer 3 ---
72
- L3_CONFIDENCE_THRESHOLD = 0.60 # ✅ Adjusted: Realistic Entry (>55)
73
  L3_WHALE_IMPACT_MAX = 0.10
74
- L3_NEWS_IMPACT_MAX = 0.05
75
- L3_MC_ADVANCED_MAX = 0.10
76
-
77
- # --- Layer 4 ---
78
- L4_ENTRY_THRESHOLD = 0.40
79
- L4_WEIGHT_ML = 0.60
80
- L4_WEIGHT_OB = 0.40
81
- L4_OB_WALL_RATIO = 0.35
82
-
83
- # --- Layer 0: Hydra & Guardian Defaults ---
84
- HYDRA_CRASH_THRESH = 0.60
85
- HYDRA_GIVEBACK_THRESH = 0.80
86
- HYDRA_STAGNATION_THRESH = 0.60
87
-
88
- # Fixed Legacy Guards
89
- LEGACY_V2_PANIC_THRESH = 0.98
90
- LEGACY_V3_HARD_THRESH = 0.95
91
- LEGACY_V3_SOFT_THRESH = 0.88
92
- LEGACY_V3_ULTRA_THRESH = 0.99
93
-
94
- @classmethod
95
- def to_dict(cls) -> Dict[str, Any]:
96
- return {k: v for k, v in cls.__dict__.items() if not k.startswith('__') and not callable(v)}
97
 
98
- # ============================================================
99
  # 🧠 MLProcessor Class
100
- # ============================================================
101
  class MLProcessor:
102
- def __init__(self, data_manager=None):
103
  self.data_manager = data_manager
104
- self.initialized = False
105
- self.initialization_attempted = False
106
 
107
- self.titan = TitanEngine(model_dir=MODELS_L2_DIR) if TitanEngine else None
108
- self.pattern_engine = ChartPatternAnalyzer(models_dir=MODELS_PATTERN_DIR) if ChartPatternAnalyzer else None
109
- self.mc_analyzer = MonteCarloEngine() if MonteCarloEngine else None
110
- self.oracle = OracleEngine(model_dir=MODELS_UNIFIED_DIR) if OracleEngine else None
111
- self.sniper = SniperEngine(models_dir=MODELS_SNIPER_DIR) if SniperEngine else None
112
 
113
- self.guardian_hydra = None
114
- if GuardianHydra:
115
- self.guardian_hydra = GuardianHydra(model_dir=MODELS_HYDRA_DIR)
116
-
117
- self.guardian_legacy = None
118
- if HybridDeepSteward:
119
- self.guardian_legacy = HybridDeepSteward(
120
- v2_model_path=MODEL_V2_PATH,
121
- v3_model_path=MODEL_V3_PATH,
122
- v3_features_map_path=MODEL_V3_FEAT
123
- )
124
-
125
- print(f"🧠 [MLProcessor V68.1] Realistic Mode Loaded (Oracle 0.60).")
126
 
127
  async def initialize(self):
128
- if self.initialized:
129
- return True
130
-
131
- # Prevent multiple initialization attempts
132
- if self.initialization_attempted:
133
- return self.initialized
134
-
135
- self.initialization_attempted = True
136
- print("⚙️ [Processor] Initializing Neural Grid...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  try:
138
- initialization_results = []
 
 
 
 
 
139
 
140
- # Initialize Titan Engine
141
- if self.titan:
142
- try:
143
- await self.titan.initialize()
144
- initialization_results.append(("Titan", True, "Success"))
145
- except Exception as e:
146
- initialization_results.append(("Titan", False, str(e)))
147
- print(f"⚠️ [Processor] Titan initialization warning: {e}")
148
 
149
- # Initialize Pattern Engine
150
- if self.pattern_engine:
151
- try:
152
- self.pattern_engine.configure_thresholds(
153
- weights=SystemLimits.PATTERN_TF_WEIGHTS,
154
- bull_thresh=SystemLimits.PATTERN_THRESH_BULLISH,
155
- bear_thresh=SystemLimits.PATTERN_THRESH_BEARISH
156
- )
157
- await self.pattern_engine.initialize()
158
- initialization_results.append(("Pattern", True, "Success"))
159
- except Exception as e:
160
- initialization_results.append(("Pattern", False, str(e)))
161
- print(f"⚠️ [Processor] Pattern engine initialization warning: {e}")
162
 
163
- # Initialize Monte Carlo Engine
164
- if self.mc_analyzer:
165
- try:
166
- # Monte Carlo engine might have an initialize method, try both approaches
167
- if hasattr(self.mc_analyzer, 'initialize'):
168
- if asyncio.iscoroutinefunction(self.mc_analyzer.initialize):
169
- await self.mc_analyzer.initialize()
170
- else:
171
- self.mc_analyzer.initialize()
172
- initialization_results.append(("MonteCarlo", True, "Success"))
173
- except Exception as e:
174
- initialization_results.append(("MonteCarlo", False, str(e)))
175
- print(f"⚠️ [Processor] Monte Carlo initialization warning: {e}")
176
 
177
- # Initialize Oracle Engine
178
- if self.oracle:
179
- try:
180
- if hasattr(self.oracle, 'set_threshold'):
181
- self.oracle.set_threshold(SystemLimits.L3_CONFIDENCE_THRESHOLD)
182
- await self.oracle.initialize()
183
- initialization_results.append(("Oracle", True, "Success"))
184
- except Exception as e:
185
- initialization_results.append(("Oracle", False, str(e)))
186
- print(f"⚠️ [Processor] Oracle initialization warning: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
187
 
188
- # Initialize Sniper Engine
189
- if self.sniper:
190
- try:
191
- if hasattr(self.sniper, 'configure_settings'):
192
- self.sniper.configure_settings(
193
- threshold=SystemLimits.L4_ENTRY_THRESHOLD,
194
- wall_ratio=SystemLimits.L4_OB_WALL_RATIO,
195
- w_ml=SystemLimits.L4_WEIGHT_ML,
196
- w_ob=SystemLimits.L4_WEIGHT_OB
197
- )
198
- await self.sniper.initialize()
199
- initialization_results.append(("Sniper", True, "Success"))
200
- except Exception as e:
201
- initialization_results.append(("Sniper", False, str(e)))
202
- print(f"⚠️ [Processor] Sniper initialization warning: {e}")
 
 
 
 
203
 
204
- # Initialize Guardian Hydra
205
- if self.guardian_hydra:
206
- try:
207
- self.guardian_hydra.initialize()
208
- initialization_results.append(("Hydra", True, "Success"))
209
- print(" 🛡️ [Guard 1] Hydra X-Ray: Active")
210
- except Exception as e:
211
- initialization_results.append(("Hydra", False, str(e)))
212
- print(f"⚠️ [Processor] Hydra initialization warning: {e}")
213
 
214
- # Initialize Legacy Guardian
215
- if self.guardian_legacy:
216
- try:
217
- if asyncio.iscoroutinefunction(self.guardian_legacy.initialize):
218
- await self.guardian_legacy.initialize()
219
- else:
220
- self.guardian_legacy.initialize()
221
-
222
- # Default init
223
- self.guardian_legacy.configure_thresholds(
224
- v2_panic=SystemLimits.LEGACY_V2_PANIC_THRESH,
225
- v3_hard=SystemLimits.LEGACY_V3_HARD_THRESH,
226
- v3_soft=SystemLimits.LEGACY_V3_SOFT_THRESH,
227
- v3_ultra=SystemLimits.LEGACY_V3_ULTRA_THRESH
228
- )
229
- initialization_results.append(("Legacy", True, "Success"))
230
- print(f" 🛡️ [Guard 2] Legacy Steward: Active")
231
- except Exception as e:
232
- initialization_results.append(("Legacy", False, str(e)))
233
- print(f"⚠️ [Processor] Legacy guardian initialization warning: {e}")
234
-
235
- # Check if critical components are initialized
236
- critical_components = ["Oracle", "Titan"]
237
- critical_initialized = True
238
- for component_name, success, _ in initialization_results:
239
- if component_name in critical_components and not success:
240
- critical_initialized = False
241
- print(f"❌ [Processor CRITICAL] {component_name} failed to initialize")
242
 
243
- if not critical_initialized:
244
- raise RuntimeError("Critical system components failed to initialize")
245
-
246
- self.initialized = True
247
- print("✅ [Processor] All Systems Operational.")
248
- return True
249
 
250
  except Exception as e:
251
- print(f"❌ [Processor FATAL] Init failed: {e}")
252
- traceback.print_exc()
253
- self.initialized = False # Ensure we don't mark as initialized on failure
254
- return False
255
 
256
- async def process_compound_signal(self, raw_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
 
 
 
257
  """
258
- L2 Processing with Hybrid Gated Scoring (Full Visibility).
259
  """
260
- if not self.initialized:
261
- init_success = await self.initialize()
262
- if not init_success:
263
- print("❌ [Processor] Cannot process signal - initialization failed")
264
- return None
265
-
266
- symbol = raw_data.get('symbol')
267
- ohlcv_data = raw_data.get('ohlcv')
268
- current_price = raw_data.get('current_price', 0.0)
269
-
270
- # ✅ الحقن المباشر للقيم
271
- limits = raw_data.get('dynamic_limits', {})
272
-
273
- if not symbol or not ohlcv_data:
274
- return None
275
-
276
  try:
277
- # 1. Titan Prediction
278
- score_titan = 0.5
279
- titan_res = {}
280
- if self.titan:
281
- try:
282
- titan_res = await asyncio.to_thread(self.titan.predict, ohlcv_data)
283
- score_titan = titan_res.get('score', 0.5)
284
- except Exception as e:
285
- print(f"⚠️ [Processor] Titan prediction error for {symbol}: {e}")
286
- score_titan = 0.5
287
-
288
- # 2. Pattern Analysis
289
- score_patterns = 0.5
290
- pattern_res = {}
291
- pattern_name = "Neutral"
292
- if self.pattern_engine:
293
- try:
294
- pattern_res = await self.pattern_engine.detect_chart_patterns(ohlcv_data)
295
- score_patterns = pattern_res.get('pattern_confidence', 0.5)
296
- pattern_name = pattern_res.get('pattern_detected', 'Neutral')
297
- except Exception as e:
298
- print(f"⚠️ [Processor] Pattern detection error for {symbol}: {e}")
299
- score_patterns = 0.5
300
-
301
- # 3. Monte Carlo Light
302
- mc_score = 0.5
303
- if self.mc_analyzer and '1h' in ohlcv_data:
304
- try:
305
- closes = [c[4] for c in ohlcv_data['1h']]
306
- raw_mc = self.mc_analyzer.run_light_check(closes)
307
- mc_score = 0.5 + (raw_mc * 5.0)
308
- mc_score = max(0.0, min(1.0, mc_score))
309
- except Exception as e:
310
- print(f"⚠️ [Processor] MC analysis error for {symbol}: {e}")
311
- mc_score = 0.5
312
 
313
- # --- 4. Hybrid Gated Logic (Aggressive) ---
 
 
 
314
 
315
- # A) Extract Gates (Injectable, fallback to Aggressive SystemLimits)
316
- gate_titan = limits.get('l2_gate_titan', SystemLimits.L2_GATE_TITAN)
317
- gate_patt = limits.get('l2_gate_pattern', SystemLimits.L2_GATE_PATTERN)
318
- gate_mc = limits.get('l2_gate_mc', SystemLimits.L2_GATE_MC)
319
 
320
- rejection_reason = None
321
- is_valid = True
322
-
323
- # B) HARD GATES Check
324
- if score_titan < gate_titan:
325
- is_valid = False
326
- rejection_reason = f"Titan {score_titan:.2f} < {gate_titan}"
327
- elif score_patterns < gate_patt:
328
- is_valid = False
329
- rejection_reason = f"Pattern {score_patterns:.2f} < {gate_patt}"
330
- elif mc_score < gate_mc:
331
- is_valid = False
332
- rejection_reason = f"MC {mc_score:.2f} < {gate_mc}"
333
-
334
- # C) Weighted Score Calculation
335
- w_titan = limits.get('w_titan', SystemLimits.L2_WEIGHT_TITAN)
336
- w_patt = limits.get('w_patt', SystemLimits.L2_WEIGHT_PATTERNS)
337
- w_mc = limits.get('w_mc', SystemLimits.L2_WEIGHT_MC)
338
 
339
- total_w = w_titan + w_patt + w_mc
340
- if total_w <= 0: total_w = 1.0
 
 
341
 
342
- hybrid_score = ((score_titan * w_titan) + (score_patterns * w_patt) + (mc_score * w_mc)) / total_w
 
 
343
 
344
- # D) Final Score Gate
345
- min_l2_score = limits.get('l2_min_score', SystemLimits.L2_MIN_SCORE)
346
- if is_valid and hybrid_score < min_l2_score:
347
- is_valid = False
348
- rejection_reason = f"Hybrid {hybrid_score:.2f} < {min_l2_score}"
349
 
350
- return {
351
- 'symbol': symbol,
352
- 'current_price': current_price,
353
- 'enhanced_final_score': hybrid_score,
354
- 'is_valid': is_valid, # Validity Flag
355
- 'rejection_reason': rejection_reason, # ✅ Reason
356
- 'dynamic_limits': limits,
357
- 'asset_regime': raw_data.get('asset_regime', 'UNKNOWN'),
358
- 'strategy_type': raw_data.get('strategy_type', 'NORMAL'),
359
- 'titan_score': score_titan,
360
- 'patterns_score': score_patterns,
361
- 'mc_score': mc_score,
362
- 'components': {
363
- 'titan_score': score_titan,
364
- 'patterns_score': score_patterns,
365
- 'mc_score': mc_score
366
- },
367
- 'pattern_name': pattern_name,
368
- 'ohlcv': ohlcv_data,
369
- 'titan_details': titan_res,
370
- 'pattern_details': pattern_res.get('details', {})
371
- }
372
- except Exception as e:
373
- print(f"❌ [Processor] Error processing {symbol}: {e}")
374
- traceback.print_exc()
375
- return None
376
-
377
- async def consult_oracle(self, symbol_data: Dict[str, Any]) -> Dict[str, Any]:
378
- if not self.initialized:
379
- init_success = await self.initialize()
380
- if not init_success:
381
- return {'action': 'WAIT', 'reason': 'System initialization failed'}
382
-
383
- # ✅ الحقن المباشر للعتبة
384
- limits = symbol_data.get('dynamic_limits', {})
385
- threshold = limits.get('l3_oracle_thresh', SystemLimits.L3_CONFIDENCE_THRESHOLD)
386
-
387
- if self.oracle:
388
- try:
389
- if hasattr(self.oracle, 'set_threshold'):
390
- self.oracle.set_threshold(threshold)
391
-
392
- decision = await self.oracle.predict(symbol_data)
393
- conf = decision.get('confidence', 0.0)
394
 
395
- if decision.get('action') in ['WATCH', 'BUY'] and conf < threshold:
396
- decision['action'] = 'WAIT'
397
- decision['reason'] = f"Context Veto: Conf {conf:.2f} < Limit {threshold:.2f}"
 
 
 
 
398
 
399
- return decision
400
- except Exception as e:
401
- print(f"❌ [Processor] Oracle consultation error: {e}")
402
- traceback.print_exc()
403
- return {'action': 'WAIT', 'reason': f'Oracle error: {str(e)}'}
404
- return {'action': 'WAIT', 'reason': 'Oracle Engine Missing'}
405
-
406
- async def check_sniper_entry(self, ohlcv_1m_data: List, order_book_data: Dict[str, Any], context_data: Dict = None) -> Dict[str, Any]:
407
- if not self.initialized:
408
- init_success = await self.initialize()
409
- if not init_success:
410
- return {'signal': 'WAIT', 'reason': 'System initialization failed'}
411
-
412
- limits = context_data.get('dynamic_limits', {}) if context_data else {}
413
- thresh = limits.get('l4_sniper_thresh', SystemLimits.L4_ENTRY_THRESHOLD)
414
- wall_r = limits.get('l4_ob_wall_ratio', SystemLimits.L4_OB_WALL_RATIO)
415
-
416
- if self.sniper:
417
- try:
418
- if hasattr(self.sniper, 'configure_settings'):
419
- self.sniper.configure_settings(
420
- threshold=thresh,
421
- wall_ratio=wall_r,
422
- w_ml=SystemLimits.L4_WEIGHT_ML,
423
- w_ob=SystemLimits.L4_WEIGHT_OB
424
- )
425
- return await self.sniper.check_entry_signal_async(ohlcv_1m_data, order_book_data)
426
- except Exception as e:
427
- print(f"❌ [Processor] Sniper entry check error: {e}")
428
- traceback.print_exc()
429
- return {'signal': 'WAIT', 'reason': f'Sniper error: {str(e)}'}
430
 
431
- return {'signal': 'WAIT', 'reason': 'Sniper Engine Missing'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
 
433
- def consult_dual_guardians(self, symbol, ohlcv_1m, ohlcv_5m, ohlcv_15m, trade_context, order_book_snapshot=None):
 
 
 
434
  """
435
- 💎 GEM-Architect: Conditional Hydra & Fixed Legacy Logic
 
 
 
 
436
  """
437
- # Ensure initialization before proceeding - FIXED: Added proper initialization check
438
- if not self.initialized:
439
- print("⚠️ [Processor] Guardians consulted before initialization")
440
- return {'action': 'HOLD', 'detailed_log': 'System not initialized', 'probs': {}, 'scores': {}}
441
-
442
- response = {'action': 'HOLD', 'detailed_log': '', 'probs': {}, 'scores': {}}
443
-
444
- # 1. استخراج الحدود الديناميكية من سياق الصفقة
445
- limits = trade_context.get('dynamic_limits', {})
446
-
447
- # ✅ سحب القيم مع Fallback آمن
448
- h_crash_thresh = limits.get('hydra_crash', SystemLimits.HYDRA_CRASH_THRESH)
449
- h_giveback_thresh = limits.get('hydra_giveback', SystemLimits.HYDRA_GIVEBACK_THRESH)
450
- h_stag_thresh = limits.get('hydra_stagnation', SystemLimits.HYDRA_STAGNATION_THRESH)
451
-
452
- # ✅ Context Data
453
- entry_price = float(trade_context.get('entry_price', 0.0))
454
- highest_price = trade_context.get('highest_price', entry_price)
455
- max_pnl_pct = ((highest_price - entry_price) / entry_price) * 100 if entry_price > 0 else 0.0
456
- time_in_trade_mins = trade_context.get('time_in_trade_mins', 0.0)
457
-
458
- # -----------------------------------------------
459
- # 1. Hydra Execution (Conditional)
460
- # -----------------------------------------------
461
- hydra_result = {'action': 'HOLD', 'reason': 'Disabled', 'probs': {}}
462
- if self.guardian_hydra and getattr(self.guardian_hydra, 'initialized', False):
463
- try:
464
- hydra_result = self.guardian_hydra.analyze_position(symbol, ohlcv_1m, ohlcv_5m, ohlcv_15m, trade_context)
465
- h_probs = hydra_result.get('probs', {})
466
-
467
- p_crash = h_probs.get('crash', 0.0)
468
- p_giveback = h_probs.get('giveback', 0.0)
469
- p_stagnation = h_probs.get('stagnation', 0.0)
470
 
471
- # 🛑 CRASH: Always Active (Safety Net)
472
- if p_crash >= h_crash_thresh:
473
- hydra_result['action'] = 'EXIT_HARD'
474
- hydra_result['reason'] = f"Hydra Crash Risk {p_crash:.2f} >= {h_crash_thresh}"
 
 
 
 
 
 
 
 
 
 
 
 
475
 
476
- # 🛑 GIVEBACK: Conditional (Profit > 0.6%)
477
- elif p_giveback >= h_giveback_thresh:
478
- if max_pnl_pct >= 0.6:
479
- hydra_result['action'] = 'EXIT_SOFT'
480
- hydra_result['reason'] = f"Hydra Giveback {p_giveback:.2f} (Max PnL {max_pnl_pct:.2f}%)"
481
- else:
482
- hydra_result['action'] = 'HOLD' # Ignore noise
483
-
484
- # 🛑 STAGNATION: Conditional (Time > 90 mins)
485
- elif p_stagnation >= h_stag_thresh:
486
- if time_in_trade_mins > 90:
487
- hydra_result['action'] = 'EXIT_SOFT'
488
- hydra_result['reason'] = f"Hydra Stagnation {p_stagnation:.2f} (>90m)"
489
- else:
490
- hydra_result['action'] = 'HOLD' # Too early
491
- except Exception as e:
492
- print(f"⚠️ [Processor] Hydra analysis error: {e}")
493
- traceback.print_exc()
494
- hydra_result = {'action': 'HOLD', 'reason': f'Hydra error: {str(e)}', 'probs': {}}
495
-
496
- # -----------------------------------------------
497
- # 2. Legacy Execution (Fixed Thresholds)
498
- # -----------------------------------------------
499
- legacy_result = {'action': 'HOLD', 'reason': 'Disabled', 'scores': {}}
500
- if self.guardian_legacy and getattr(self.guardian_legacy, 'initialized', False):
501
- try:
502
- self.guardian_legacy.configure_thresholds(
503
- v2_panic=0.98,
504
- v3_hard=0.95,
505
- v3_soft=0.88,
506
- v3_ultra=0.99
507
- )
508
- vol_30m = trade_context.get('volume_30m_usd', 0.0)
509
- legacy_result = self.guardian_legacy.analyze_position(
510
- ohlcv_1m, ohlcv_5m, ohlcv_15m, entry_price,
511
- order_book=order_book_snapshot,
512
- volume_30m_usd=vol_30m
513
- )
514
- except Exception as e:
515
- print(f"⚠️ [Processor] Legacy guardian analysis error: {e}")
516
- traceback.print_exc()
517
- legacy_result = {'action': 'HOLD', 'reason': f'Legacy error: {str(e)}', 'scores': {}}
518
-
519
- # -----------------------------------------------
520
- # 3. Final Arbitration
521
- # -----------------------------------------------
522
- h_probs = hydra_result.get('probs', {})
523
- l_scores = legacy_result.get('scores', {})
524
-
525
- h_c = h_probs.get('crash', 0.0)
526
- h_g = h_probs.get('giveback', 0.0)
527
- l_v2 = l_scores.get('v2', 0.0)
528
-
529
- stamp_str = f"🐲[C:{h_c:.2f}|G:{h_g:.2f}] 🕸️[V2:{l_v2:.2f}]"
530
-
531
- final_action = 'HOLD'
532
- final_reason = f"Safe. {stamp_str}"
533
-
534
- # Ensure both results have action keys before comparison
535
- hydra_action = hydra_result.get('action', 'HOLD')
536
- legacy_action = legacy_result.get('action', 'HOLD')
537
-
538
- if hydra_action in ['EXIT_HARD', 'EXIT_SOFT', 'TIGHTEN_SL', 'TRAIL_SL']:
539
- final_action = hydra_action
540
- final_reason = f"🐲 HYDRA: {hydra_result.get('reason', 'Unknown Hydra action')}"
541
- elif legacy_action in ['EXIT_HARD', 'EXIT_SOFT']:
542
- final_action = legacy_action
543
- final_reason = f"🕸️ LEGACY: {legacy_result.get('reason', 'Unknown Legacy action')}"
544
-
545
- return {
546
- 'action': final_action,
547
- 'reason': final_reason,
548
- 'detailed_log': f"{final_action} | {stamp_str}",
549
- 'probs': h_probs,
550
- 'scores': l_scores
551
- }
552
 
553
- async def run_advanced_monte_carlo(self, symbol, timeframe='1h'):
554
- if self.mc_analyzer and self.data_manager:
555
- try:
556
- ohlcv = await self.data_manager.get_latest_ohlcv(symbol, timeframe, limit=300)
557
- if ohlcv:
558
- return self.mc_analyzer.run_advanced_simulation([c[4] for c in ohlcv])
559
- except Exception as e:
560
- print(f"⚠️ [Processor] Advanced MC error for {symbol}: {e}")
561
- traceback.print_exc()
562
- pass
563
- return 0.0
 
 
 
1
+ # ==============================================================================
2
+ # 🧠 ml_engine/processor.py (V67.5 - GEM-Architect: The Central Nervous System)
3
+ # ==============================================================================
4
+ # GEM-Architect Approved
5
+ # - Integrates Titan V3 (PyTorch), Oracle V4.5 (LGBM), and Patterns V30.
6
+ # - Ensures 'titan_probs' is passed correctly to Oracle.
7
+ # - Implements Full L2/L3/L4 Logic pipelines.
8
+ # ==============================================================================
9
 
10
  import asyncio
11
  import traceback
 
 
 
12
  import numpy as np
13
+ import pandas as pd
14
+ from datetime import datetime
15
  from typing import Dict, Any, List, Optional
16
 
17
+ # --- Import Engines ---
18
+ try:
19
+ from ml_engine.titan_engine import TitanEngine
20
+ from ml_engine.patterns import ChartPatternAnalyzer
21
+ from ml_engine.oracle_engine import OracleEngine
22
+ from ml_engine.monte_carlo import MonteCarloEngine
23
+ except ImportError as e:
24
+ print(f"❌ [Processor] Import Error: {e}")
25
+
26
+ # ==============================================================================
27
+ # ⚙️ System Limits (Global Configuration)
28
+ # ==============================================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  class SystemLimits:
30
+ # --- General ---
31
+ MAX_CONCURRENT_TRADES = 5
 
 
32
 
33
+ # --- Layer 2 (Compound Signal) ---
34
+ L2_MIN_SCORE_REQ = 0.55 # Minimum score to pass L2
35
 
36
+ # --- Layer 3 (Oracle) ---
37
+ L3_CONFIDENCE_THRESHOLD = 0.005 # 0.5% Expected Return (Golden Threshold)
38
+ L3_NEWS_IMPACT_MAX = 0.15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  L3_WHALE_IMPACT_MAX = 0.10
40
+ L3_MC_ADVANCED_MAX = 0.10
41
+
42
+ # --- Layer 4 (Sniper) ---
43
+ L4_ENTRY_THRESHOLD = 0.70 # Sniper micro-structure confidence
44
+
45
+ # --- Risk Management ---
46
+ RISK_REWARD_RATIO = 2.0
47
+ MAX_DAILY_DRAWDOWN = 0.05
48
+
49
+ # --- Regimes ---
50
+ CURRENT_REGIME = "NEUTRAL"
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ # ==============================================================================
53
  # 🧠 MLProcessor Class
54
+ # ==============================================================================
55
  class MLProcessor:
56
+ def __init__(self, data_manager):
57
  self.data_manager = data_manager
 
 
58
 
59
+ # --- Engines ---
60
+ self.titan = TitanEngine()
61
+ self.patterns = ChartPatternAnalyzer()
62
+ self.oracle = OracleEngine()
63
+ self.monte_carlo = MonteCarloEngine()
64
 
65
+ self.initialized = False
66
+ print("🧠 [Processor V67.5] System Core Created.")
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  async def initialize(self):
69
+ """Initialize all sub-engines"""
70
+ if self.initialized: return
71
+
72
+ print("🚀 [Processor] Booting up Neural Engines...")
73
+
74
+ # 1. Titan (CNN)
75
+ await self.titan.initialize()
76
+
77
+ # 2. Patterns (XGB)
78
+ await self.patterns.initialize()
79
+ # Inject Dynamic Thresholds for Patterns
80
+ self.patterns.configure_thresholds(
81
+ weights={'15m': 0.4, '1h': 0.3, '5m': 0.2, '4h': 0.1},
82
+ bull_thresh=0.6,
83
+ bear_thresh=0.4
84
+ )
85
+
86
+ # 3. Oracle (LGBM)
87
+ await self.oracle.initialize()
88
+
89
+ self.initialized = True
90
+ print("✅ [Processor] All Systems Online.")
91
+
92
+ # ==========================================================================
93
+ # 🧬 Layer 2: The Compound Signal Processor
94
+ # ==========================================================================
95
+ async def process_compound_signal(self, raw_input: Dict[str, Any]) -> Optional[Dict[str, Any]]:
96
+ """
97
+ Takes raw OHLCV data, runs Titan + Patterns + MC_Light.
98
+ Returns a 'Signal Object' ready for Oracle.
99
+ """
100
+ if not self.initialized: await self.initialize()
101
+
102
+ symbol = raw_input.get('symbol')
103
+ ohlcv = raw_input.get('ohlcv')
104
+ current_price = raw_input.get('current_price')
105
+
106
  try:
107
+ # 1. Titan Analysis (The Trend Hunter)
108
+ # -----------------------------------
109
+ # Titan V3 returns {'score': float, 'probs': [neu, loss, win], 'status': str}
110
+ titan_res = self.titan.predict(ohlcv)
111
+ titan_score = titan_res.get('score', 0.0)
112
+ titan_probs = titan_res.get('probs', [0.0, 0.0, 0.0]) # ✅ CRITICAL: Capture Probs
113
 
114
+ # 2. Pattern Analysis (The Chart Reader)
115
+ # -----------------------------------
116
+ patt_res = await self.patterns.detect_chart_patterns(ohlcv)
117
+ patt_score = patt_res.get('pattern_confidence', 0.0)
 
 
 
 
118
 
119
+ # 3. Monte Carlo Light (The Risk Checker)
120
+ # -----------------------------------
121
+ # Use 1h closes for light check
122
+ closes_1h = []
123
+ if '1h' in ohlcv:
124
+ closes_1h = [c[4] for c in ohlcv['1h']]
125
+ mc_score = self.monte_carlo.run_light_check(closes_1h)
 
 
 
 
 
 
126
 
127
+ # 4. Synthesis (Weighted Average for L2 Score)
128
+ # -----------------------------------
129
+ # Weights: Titan (50%), Patterns (30%), MC (20%)
130
+ hybrid_score = (titan_score * 0.50) + (patt_score * 0.30) + (max(0, mc_score) * 0.20)
 
 
 
 
 
 
 
 
 
131
 
132
+ # 5. Build Signal Object
133
+ # -----------------------------------
134
+ l2_signal = {
135
+ 'symbol': symbol,
136
+ 'current_price': current_price,
137
+ 'ohlcv': ohlcv, # Oracle needs raw data for context
138
+
139
+ # Model Scores
140
+ 'titan_score': titan_score,
141
+ 'titan_probs': titan_probs, # PASSED TO ORACLE
142
+ 'patterns_score': patt_score,
143
+ 'mc_score': mc_score,
144
+
145
+ # Hybrid Result
146
+ 'enhanced_final_score': hybrid_score,
147
+ 'is_valid': hybrid_score >= SystemLimits.L2_MIN_SCORE_REQ,
148
+
149
+ # Passthrough Metadata
150
+ 'dynamic_limits': raw_input.get('dynamic_limits', {}),
151
+ 'asset_regime': raw_input.get('asset_regime', 'UNKNOWN'),
152
+ 'strategy_tag': raw_input.get('strategy_tag', 'NONE'),
153
+ 'timestamp': datetime.now().isoformat()
154
+ }
155
 
156
+ if not l2_signal['is_valid']:
157
+ l2_signal['rejection_reason'] = f"Low L2 Score ({hybrid_score:.2f} < {SystemLimits.L2_MIN_SCORE_REQ})"
158
+
159
+ return l2_signal
160
+
161
+ except Exception as e:
162
+ print(f"❌ [Processor] L2 Error ({symbol}): {e}")
163
+ traceback.print_exc()
164
+ return None
165
+
166
+ # ==========================================================================
167
+ # 🔮 Layer 3: The Oracle Consultant
168
+ # ==========================================================================
169
+ async def consult_oracle(self, l2_signal: Dict[str, Any]) -> Dict[str, Any]:
170
+ """
171
+ Passes the L2 Signal to Oracle Engine for Final Decision.
172
+ """
173
+ if not l2_signal.get('is_valid', False):
174
+ return {'action': 'WAIT', 'confidence': 0.0, 'reason': 'L2 Rejected'}
175
 
176
+ try:
177
+ # Oracle V4.5 expects 'titan_probs' and 'ohlcv'
178
+ decision = await self.oracle.predict(l2_signal)
 
 
 
 
 
 
179
 
180
+ # Merge decision into signal
181
+ l2_signal.update(decision)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
+ return decision
 
 
 
 
 
184
 
185
  except Exception as e:
186
+ print(f"❌ [Processor] Oracle Error: {e}")
187
+ return {'action': 'WAIT', 'confidence': 0.0, 'reason': 'Oracle Crash'}
 
 
188
 
189
+ # ==========================================================================
190
+ # 🔭 Layer 4: The Sniper Entry (Micro-Structure)
191
+ # ==========================================================================
192
+ async def check_sniper_entry(self, ohlcv_1m: List, order_book: Dict, context_data: Dict = None) -> Dict[str, Any]:
193
  """
194
+ Analyzes 1-minute data and Order Book for the perfect entry tick.
195
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  try:
197
+ if not ohlcv_1m or len(ohlcv_1m) < 20:
198
+ return {'signal': 'WAIT', 'confidence_prob': 0.0, 'reason': 'No 1m Data'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
+ # 1. Parse Data
201
+ closes = np.array([float(x[4]) for x in ohlcv_1m])
202
+ vols = np.array([float(x[5]) for x in ohlcv_1m])
203
+ current_price = closes[-1]
204
 
205
+ # 2. RSI 1m Calculation (Fast)
206
+ delta = np.diff(closes)
207
+ gain = np.where(delta > 0, delta, 0)
208
+ loss = np.where(delta < 0, -delta, 0)
209
 
210
+ avg_gain = np.mean(gain[-14:]) if len(gain) >= 14 else 0
211
+ avg_loss = np.mean(loss[-14:]) if len(loss) >= 14 else 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
 
213
+ if avg_loss == 0: rsi_1m = 100
214
+ else:
215
+ rs = avg_gain / avg_loss
216
+ rsi_1m = 100 - (100 / (1 + rs))
217
 
218
+ # 3. Order Book Imbalance (Buying Pressure)
219
+ bids = order_book.get('bids', [])
220
+ asks = order_book.get('asks', [])
221
 
222
+ bid_vol = sum([float(x[1]) for x in bids[:10]]) # Top 10 levels
223
+ ask_vol = sum([float(x[1]) for x in asks[:10]])
 
 
 
224
 
225
+ imbalance = 0.5
226
+ if (bid_vol + ask_vol) > 0:
227
+ imbalance = bid_vol / (bid_vol + ask_vol) # > 0.5 means Buying Pressure
228
+
229
+ # 4. Volume Spike Check
230
+ vol_ma = np.mean(vols[-20:])
231
+ vol_spike = (vols[-1] / vol_ma) if vol_ma > 0 else 1.0
232
+
233
+ # 5. Logic Gates
234
+ score = 0.5
235
+ reason = []
236
+
237
+ # A. RSI Condition (Not Overbought)
238
+ if rsi_1m < 70:
239
+ score += 0.1
240
+ if rsi_1m < 30: score += 0.1 # Oversold bounce
241
+ else:
242
+ score -= 0.1
243
+ reason.append("RSI Overbought")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
+ # B. Order Book Condition
246
+ if imbalance > 0.6:
247
+ score += 0.15
248
+ reason.append("Strong Bids")
249
+ elif imbalance < 0.4:
250
+ score -= 0.1
251
+ reason.append("Wall Resistance")
252
 
253
+ # C. Volume Condition
254
+ if vol_spike > 1.5:
255
+ score += 0.1
256
+ reason.append("Vol Spike")
257
+
258
+ # D. Context Boost (From Oracle)
259
+ oracle_conf = context_data.get('confidence', 0.0) if context_data else 0.0
260
+ if oracle_conf > 0.8: score += 0.1
261
+
262
+ # Final Decision
263
+ final_score = min(1.0, max(0.0, score))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
+ if final_score >= SystemLimits.L4_ENTRY_THRESHOLD:
266
+ return {
267
+ 'signal': 'BUY',
268
+ 'entry_price': current_price,
269
+ 'confidence_prob': final_score,
270
+ 'reason': f"Sniper GO ({', '.join(reason)})"
271
+ }
272
+ else:
273
+ return {
274
+ 'signal': 'WAIT',
275
+ 'entry_price': 0.0,
276
+ 'confidence_prob': final_score,
277
+ 'reason': f"Weak Micro ({', '.join(reason)})"
278
+ }
279
+
280
+ except Exception as e:
281
+ return {'signal': 'WAIT', 'confidence_prob': 0.0, 'reason': f"Sniper Err: {e}"}
282
 
283
+ # ==========================================================================
284
+ # 🛡️ Post-Entry: Dual Guardians (The Sentry)
285
+ # ==========================================================================
286
+ def consult_dual_guardians(self, symbol: str, d1: list, d5: list, d15: list, context: Dict, order_book_snapshot: Dict = None) -> Dict[str, Any]:
287
  """
288
+ Real-time Trade Management Logic (Hydra).
289
+ Checks for:
290
+ 1. Crash/Panic (Hard Exit)
291
+ 2. Stagnation (Time Exit)
292
+ 3. Profit Giveback (Trailing Logic)
293
  """
294
+ try:
295
+ if not d1 or not d5: return {'action': 'HOLD', 'reason': 'No Data'}
296
+
297
+ current_price = float(d1[-1][4])
298
+ entry_price = float(context.get('entry_price', 0))
299
+ highest_price = float(context.get('highest_price', entry_price))
300
+ time_in_trade = float(context.get('time_in_trade_mins', 0))
301
+
302
+ pnl_pct = (current_price - entry_price) / entry_price
303
+ max_pnl_pct = (highest_price - entry_price) / entry_price
304
+
305
+ # --- 1. The Panic Guard (Crash Protection) ---
306
+ # If price drops fast with high volume
307
+ last_candle_drop = (float(d1[-1][1]) - float(d1[-1][4])) / float(d1[-1][1]) # Open - Close
308
+ vol_spike = 1.0 # Placeholder calculation
309
+
310
+ if last_candle_drop > 0.015: # 1.5% drop in 1 minute
311
+ return {'action': 'EXIT_HARD', 'reason': 'Flash Crash Detected'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
 
313
+ # --- 2. The Profit Guard (Trailing) ---
314
+ # If we were in good profit (>1.5%) and lost 40% of it
315
+ if max_pnl_pct > 0.015:
316
+ drawdown_from_peak = (highest_price - current_price) / highest_price
317
+ if drawdown_from_peak > (max_pnl_pct * 0.4):
318
+ return {'action': 'EXIT_SOFT', 'reason': 'Profit Giveback > 40%'}
319
+
320
+ # --- 3. The Time Guard (Stagnation) ---
321
+ # If 45 mins passed and PnL is barely moving (-0.2% to +0.2%)
322
+ if time_in_trade > 45 and -0.002 < pnl_pct < 0.002:
323
+ return {'action': 'EXIT_SOFT', 'reason': 'Stagnation (Dead Money)'}
324
+
325
+ # --- 4. Smart SL (Tightening) ---
326
+ # If we reached TP1 (approx 1%), move SL to Breakeven
327
+ if pnl_pct > 0.01:
328
+ return {'action': 'TIGHTEN_SL', 'reason': 'Secure Breakeven'}
329
 
330
+ return {'action': 'HOLD', 'reason': 'Monitoring'}
331
+
332
+ except Exception as e:
333
+ return {'action': 'HOLD', 'reason': f"Guard Err: {e}"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
 
335
+ # ==========================================================================
336
+ # 🎲 Advanced Helpers
337
+ # ==========================================================================
338
+ async def run_advanced_monte_carlo(self, symbol: str, tf: str = '1h') -> float:
339
+ """Wrapper for MC Tier 2"""
340
+ try:
341
+ # Note: This requires DataManager to fetch deep history,
342
+ # usually called from App/TradeManager logic context.
343
+ # Here we assume data is passed or we return a placeholder
344
+ # if we can't fetch inside processor easily without async IO loop overhead.
345
+ # Ideally, DataManager calls this with data.
346
+ return 0.0 # Placeholder for direct call pattern
347
+ except: return 0.0