Riy777 commited on
Commit
3a93a42
·
1 Parent(s): 624c411

Update ml_engine/processor.py

Browse files
Files changed (1) hide show
  1. ml_engine/processor.py +126 -268
ml_engine/processor.py CHANGED
@@ -1,307 +1,165 @@
1
- # ml_engine/processor.py (Updated to V6.6 - Statistical News Score)
2
  import pandas as pd
3
  import numpy as np
4
- from datetime import datetime
5
  import asyncio
6
  import json
7
- import re
8
 
9
- # (Import components from the same folder)
10
  from .indicators import AdvancedTechnicalAnalyzer
11
  from .monte_carlo import MonteCarloAnalyzer
12
- from .patterns import ChartPatternAnalyzer
13
  from .strategies import MultiStrategyEngine
14
 
15
  class MLProcessor:
16
- def __init__(self, market_context, data_manager, learning_hub): # (Changed from learning_engine)
17
  self.market_context = market_context
18
  self.data_manager = data_manager
19
- self.learning_hub = learning_hub # (Changed from learning_engine)
20
 
21
  self.technical_analyzer = AdvancedTechnicalAnalyzer()
22
- # (Pass the hub to the strategy engine)
23
- self.strategy_engine = MultiStrategyEngine(data_manager, learning_hub)
24
  self.monte_carlo_analyzer = MonteCarloAnalyzer()
25
 
26
- # 🔴 --- START OF CHANGE (V6.4) --- 🔴
27
- # (استخدام محرك V8 الرئيسي المحمل مسبقاً من DataManager)
28
- # (هذا يحل مشكلة "النموذج/المقياس غير محمل")
29
  if self.data_manager and self.data_manager.pattern_analyzer:
30
- self.pattern_analyzer = self.data_manager.pattern_analyzer
31
- print("✅ [MLProcessor V6.4] تم ربط محرك الأنماط V8 (الرئيسي).")
32
  else:
33
- print("⚠️ [MLProcessor V6.4] DataManager أو محرك V8 غير متاح. العودة إلى الوضع الآمن.")
34
- self.pattern_analyzer = ChartPatternAnalyzer(r2_service=None)
35
- # 🔴 --- END OF CHANGE --- 🔴
36
-
37
- self.whale_data_semaphore = asyncio.Semaphore(2)
38
 
39
  async def process_and_score_symbol_enhanced(self, raw_data, preloaded_whale_data: dict = None):
40
  """
41
- (Unchanged logic, but now self.strategy_engine uses the learning_hub)
42
  """
43
  try:
44
- if not raw_data or not raw_data.get('ohlcv'):
45
- return None
46
-
47
- symbol = raw_data['symbol']
48
-
49
- base_analysis = await self.process_and_score_symbol(raw_data)
50
- if not base_analysis:
51
- return None
52
 
53
- try:
54
- # Calculate Indicators
55
- advanced_indicators = {}
56
- ohlcv_available = raw_data.get('ohlcv', {})
57
- for timeframe, candles in ohlcv_available.items():
58
- if candles and len(candles) >= 20:
59
- dataframe = self._create_dataframe(candles)
60
- indicators = self.technical_analyzer.calculate_all_indicators(dataframe, timeframe)
61
- advanced_indicators[timeframe] = indicators
62
- base_analysis['advanced_indicators'] = advanced_indicators
63
-
64
- # Monte Carlo (Phase 1)
65
- monte_carlo_results = await self.monte_carlo_analyzer.generate_1h_price_distribution(ohlcv_available)
66
-
67
- if monte_carlo_results:
68
- base_analysis['monte_carlo_distribution'] = monte_carlo_results
69
- base_analysis['monte_carlo_probability'] = monte_carlo_results.get('probability_of_gain', 0)
70
- base_analysis['monte_carlo_details'] = self.monte_carlo_analyzer.simulation_results
71
- else:
72
- base_analysis['monte_carlo_distribution'] = None
73
- base_analysis['monte_carlo_probability'] = 0
74
- base_analysis['monte_carlo_details'] = self.monte_carlo_analyzer.simulation_results
75
-
76
- # Pattern Analysis
77
- # (This call will now use the *correctly loaded* V8 engine)
78
- pattern_analysis = await self.pattern_analyzer.detect_chart_patterns(ohlcv_available)
79
- base_analysis['pattern_analysis'] = pattern_analysis
80
-
81
- # Whale Data
82
- if preloaded_whale_data:
83
- base_analysis['whale_data'] = preloaded_whale_data.get(symbol, {'data_available': False, 'reason': 'Not preloaded'})
84
- else:
85
- base_analysis['whale_data'] = {'data_available': False, 'reason': 'Preloading disabled'}
86
-
87
- # 🔴 (This call now uses the Learning Hub via strategy_engine)
88
- strategy_scores, base_scores = await self.strategy_engine.evaluate_all_strategies(base_analysis, self.market_context)
89
- base_analysis['strategy_scores'] = strategy_scores
90
- base_analysis['base_strategy_scores'] = base_scores
91
-
92
- if base_scores:
93
- best_strategy = max(base_scores.items(), key=lambda x: x[1])
94
- best_strategy_name = best_strategy[0]
95
- best_strategy_score = best_strategy[1]
96
- base_analysis['recommended_strategy'] = best_strategy_name
97
- base_analysis['strategy_confidence'] = best_strategy_score
98
- base_analysis['target_strategy'] = best_strategy_name if best_strategy_score > 0.3 else 'GENERIC'
99
-
100
- # 🔴 (V6.6) استدعاء الدالة المحدثة (بدون درجة الأخبار هنا، لأنها تضاف لاحقاً)
101
- enhanced_score = self._calculate_enhanced_final_score(base_analysis)
102
- base_analysis['enhanced_final_score'] = enhanced_score
103
-
104
- return base_analysis
105
-
106
- except Exception as strategy_error:
107
- print(f"❌ Error in advanced analysis for {symbol}: {strategy_error}")
108
- return base_analysis
109
-
110
- except Exception as error:
111
- print(f"❌ Fatal error in enhanced processing for {raw_data.get('symbol', 'unknown')}: {error}")
112
- return None
113
-
114
- def _create_dataframe(self, candles):
115
- # (This function remains unchanged)
116
- try:
117
- if not candles: return pd.DataFrame()
118
- df = pd.DataFrame(candles, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
119
- df[['open', 'high', 'low', 'close', 'volume']] = df[['open', 'high', 'low', 'close', 'volume']].astype(float)
120
- df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
121
- df.set_index('timestamp', inplace=True)
122
- df.sort_index(inplace=True)
123
- return df
124
- except Exception as e:
125
- print(f"❌ Error creating DataFrame: {e}")
126
- return pd.DataFrame()
127
 
128
- # 🔴 --- START OF CHANGE (V6.6 - STATISTICAL NEWS SCORE) --- 🔴
129
- def _calculate_enhanced_final_score(self, analysis):
130
- """(محدث V6.6) استخدام درجة الأخبار الإحصائية (الربح/الخسارة الفعلي) بدلاً من VADER الخام."""
131
- try:
132
- base_score = analysis.get('final_score', 0)
133
- pattern_confidence = analysis.get('pattern_analysis', {}).get('pattern_confidence', 0)
134
- strategy_confidence = analysis.get('strategy_confidence', 0)
135
-
136
- # (جديد V6.6) جلب درجة الأخبار الإحصائية (الربح/الخسارة الفعلي)
137
- # (هذه القيمة ستكون 0.0 لمعظم العمليات، إلا عند إعادة حساب Top 10)
138
- # (القيمة هي PnL%، مثلاً: 1.1% أو -0.5%)
139
- statistical_pnl = analysis.get('statistical_news_pnl', 0.0)
140
-
141
- # (تطبيع PnL% إلى درجة من 0 إلى 1)
142
- # نفترض أن أقصى تأثير هو -3% إلى +3%
143
- clamped_pnl = max(min(statistical_pnl, 3.0), -3.0)
144
- # (-3% -> 0.0)
145
- # (0% -> 0.5)
146
- # (+3% -> 1.0)
147
- normalized_news_score = (clamped_pnl + 3.0) / 6.0
148
-
149
- # --- 1. حساب درجة مونت كارلو (كما في V6.2) ---
150
- mc_distribution = analysis.get('monte_carlo_distribution')
151
- monte_carlo_score = 0
152
-
153
- if mc_distribution:
154
- prob_gain = mc_distribution.get('probability_of_gain', 0)
155
- var_95_value = mc_distribution.get('risk_metrics', {}).get('VaR_95_value', 0)
156
- current_price = analysis.get('current_price', 1)
157
-
158
- if current_price > 0:
159
- normalized_var = var_95_value / current_price
160
- risk_penalty = 1.0
161
- if normalized_var > 0.05: risk_penalty = 0.5
162
- elif normalized_var > 0.03: risk_penalty = 0.8
163
-
164
- normalized_prob_score = max(0.0, (prob_gain - 0.5) * 2)
165
- monte_carlo_score = normalized_prob_score * risk_penalty
166
- else:
167
- monte_carlo_score = 0
168
-
169
- # --- 2. حساب درجة الحيتان (كما في V6.2) ---
170
- whale_confidence = 0
171
- whale_data = analysis.get('whale_data')
172
- if whale_data and whale_data.get('data_available'):
173
- signal = whale_data.get('trading_signal', {})
174
- if signal.get('action') != 'HOLD' and signal.get('confidence', 0) >= 0.5:
175
- whale_confidence = signal.get('confidence', 0)
176
 
177
- # --- 3. حساب النتيجة الموزونة الأولية (محدث V6.5 بالأوزان الجديدة) ---
178
- components = []
179
- weights = []
 
 
 
 
 
180
 
181
- # (الأوزان الجديدة التي تدمج الأخبار بـ 0.10)
182
- if base_score > 0: components.append(base_score); weights.append(0.15)
183
- if monte_carlo_score > 0: components.append(monte_carlo_score); weights.append(0.20)
184
- if pattern_confidence > 0: components.append(pattern_confidence); weights.append(0.20)
185
- if strategy_confidence > 0: components.append(strategy_confidence); weights.append(0.15)
186
- if whale_confidence > 0: components.append(whale_confidence); weights.append(0.20)
187
 
188
- # (V6.6) إضافة درجة الأخبار الإحصائية المطبعة (Normalized) بالوزن 0.10
189
- # (ستكون 0.5 إذا كانت الدرجة الافتراضية 0، وهو ما يعني "محايد" بوزن 0.10)
190
- components.append(normalized_news_score); weights.append(0.10)
 
 
191
 
 
 
 
192
 
193
- if not components: return 0
194
- total_weight = sum(weights)
195
- if total_weight == 0: return 0
196
- enhanced_score = sum(comp * weight for comp, weight in zip(components, weights)) / total_weight
197
-
198
- # --- 4. ( V6.3) تطبيق 'عامل جزاء الإرهاق' ---
199
- exhaustion_penalty_factor = 1.0
200
-
201
- # (جلب البيانات المطلوبة من القاموس)
202
- price_change_24h = analysis.get('price_change_24h', 0)
203
- rsi_1d = analysis.get('advanced_indicators', {}).get('1d', {}).get('rsi', 50)
204
- rsi_4h = analysis.get('advanced_indicators', {}).get('4h', {}).get('rsi', 50)
205
-
206
- if price_change_24h > 60 and (rsi_1d > 80 or rsi_4h > 80):
207
- exhaustion_penalty_factor = 0.4 # (عقوبة قاسية - 60%)
208
- elif price_change_24h > 40 and (rsi_1d > 75 or rsi_4h > 75):
209
- exhaustion_penalty_factor = 0.6 # (عقوبة متوسطة - 40%)
210
-
211
- if exhaustion_penalty_factor < 1.0:
212
- # (اختياري: للطباعة التشخيصية)
213
- # print(f" ⚠️ [Processor] {analysis.get('symbol')} Exhaustion Penalty Applied! Score {enhanced_score:.2f} * {exhaustion_penalty_factor} (24h: {price_change_24h:+.1f}%, RSI 1D: {rsi_1d:.1f})")
214
- pass
215
 
216
- final_penalized_score = enhanced_score * exhaustion_penalty_factor
217
-
218
- return min(max(final_penalized_score, 0.0), 1.0)
219
-
220
  except Exception as e:
221
- print(f"❌ Error calculating enhanced score: {e}")
222
- return analysis.get('final_score', 0)
223
- # 🔴 --- END OF CHANGE --- 🔴
224
-
225
- async def process_and_score_symbol(self, raw_data):
226
- """(محدث V6.3) إضافة price_change_24h إلى القاموس"""
227
- try:
228
- symbol = raw_data['symbol']
229
- ohlcv_data = raw_data.get('ohlcv')
230
- if not ohlcv_data: return None
231
- current_price = raw_data.get('current_price', 0)
232
- layer1_score = raw_data.get('layer1_score', 0)
233
- reasons = raw_data.get('reasons_for_candidacy', [])
234
- final_score = layer1_score
235
- successful_timeframes = raw_data.get('successful_timeframes', 0)
236
-
237
- # 🔴 --- START OF CHANGE (V6.3) --- 🔴
238
- price_change_24h = raw_data.get('price_change_24h', 0)
239
- # 🔴 --- END OF CHANGE --- 🔴
240
-
241
- return {
242
- 'symbol': symbol, 'current_price': current_price, 'final_score': final_score,
243
- 'enhanced_final_score': final_score, 'reasons_for_candidacy': reasons,
244
- 'layer1_score': layer1_score, 'ohlcv': ohlcv_data,
245
- 'successful_timeframes': successful_timeframes,
246
- 'price_change_24h': price_change_24h # (إضافة الحقل هنا)
247
- }
248
- except Exception as error:
249
- print(f"❌ Error in basic symbol processing {raw_data.get('symbol', 'unknown')}: {error}")
250
  return None
251
 
252
- def filter_top_candidates(self, candidates, number_of_candidates=10):
253
- # (This function remains unchanged)
254
- valid_candidates = [c for c in candidates if c is not None and isinstance(c, dict)]
255
- if not valid_candidates: print("❌ No valid candidates to filter"); return []
256
- sorted_candidates = sorted(valid_candidates, key=lambda c: c.get('enhanced_final_score', 0), reverse=True)
257
- top_candidates = sorted_candidates[:number_of_candidates]
258
-
259
- print(f"🎖️ Top {len(top_candidates)} Candidates:")
260
- for i, c in enumerate(top_candidates):
261
- score = c.get('enhanced_final_score', 0); strategy = c.get('recommended_strategy', 'GENERIC'); mc_dist = c.get('monte_carlo_distribution'); pattern = c.get('pattern_analysis', {}).get('pattern_detected', 'no_pattern'); symbol = c.get('symbol', 'UNKNOWN'); timeframes = c.get('successful_timeframes', 0)
262
- print(f" {i+1}. {symbol}: 📊 {score:.3f} | TFs: {timeframes}/6")
263
- if mc_dist:
264
- mc_pi_90 = mc_dist.get('prediction_interval_90', [0,0]); mc_var = mc_dist.get('risk_metrics', {}).get('VaR_95_value', 0)
265
- print(f" 🎯 MonteCarlo: 90% PI [{mc_pi_90[0]:.4f} - {mc_pi_90[1]:.4f}] | VaR: ${mc_var:.4f}")
266
- print(f" 🎯 Strategy: {strategy} | Pattern: {pattern}")
267
- whale_data = c.get('whale_data')
268
- if whale_data and whale_data.get('data_available'):
269
- signal = whale_data.get('trading_signal', {}); print(f" 🐋 Whale: {signal.get('action', 'HOLD')} (Conf: {signal.get('confidence', 0):.2f})")
270
- # (V6.6) طباعة درجة الأخبار الإحصائية إن وجدت
271
- if 'statistical_news_pnl' in c:
272
- print(f" 📰 News (Learned PnL): {c['statistical_news_pnl']:.2f}%")
273
- return top_candidates
274
 
275
- async def process_multiple_symbols_parallel(self, symbols_data_list, preloaded_whale_data: dict, max_concurrent=5):
276
- # (This function remains unchanged)
277
- semaphore = asyncio.Semaphore(max_concurrent)
278
- tasks_results = []
279
- async def process_symbol_with_semaphore(symbol_data):
280
- async with semaphore:
281
- try:
282
- return await self.process_and_score_symbol_enhanced(symbol_data, preloaded_whale_data)
283
- except Exception as e:
284
- return e
285
  try:
286
- batch_tasks = [asyncio.create_task(process_symbol_with_semaphore(sd)) for sd in symbols_data_list]
287
- batch_results = await asyncio.gather(*batch_tasks, return_exceptions=False)
288
- successful_results = []
289
- for result in batch_results:
290
- if isinstance(result, Exception): raise result
291
- if isinstance(result, dict): successful_results.append(result)
292
- return successful_results
293
- except Exception as error:
294
- raise error
 
 
 
 
 
295
 
296
- def safe_json_parse(json_string):
297
- # (This function remains unchanged)
298
- if not json_string: return None
299
- try:
300
- return json.loads(json_string)
301
- except json.JSONDecodeError:
 
302
  try:
303
- s = str(json_string).replace("'", '"'); s = re.sub(r'\\"', '"', s); s = re.sub(r'[\n\t]', ' ', s); s = re.sub(r'(?<!")(\b\w+\b)(?=\s*:)', r'"\1"', s); s = re.sub(r':\s*(\btrue\b|\bfalse\b|\bnull\b)(?=[,\s}])', r': \1', s); s = re.sub(r',\s*([}\]])', r'\1', s)
304
- return json.loads(s)
305
- except json.JSONDecodeError: return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
 
307
- print("✅ ML Processor loaded - V6.6 (Statistical News Score)")
 
 
 
 
 
 
 
 
 
1
+ # ml_engine/processor.py (V11.0 - XGBoost Multi-TF Integrated & New Scoring)
2
  import pandas as pd
3
  import numpy as np
 
4
  import asyncio
5
  import json
6
+ import traceback
7
 
8
+ # استيراد المحركات من نفس المجلد
9
  from .indicators import AdvancedTechnicalAnalyzer
10
  from .monte_carlo import MonteCarloAnalyzer
11
+ # لا نحتاج لاستيراد ChartPatternAnalyzer هنا لأنه سيأتي جاهزاً من DataManager
12
  from .strategies import MultiStrategyEngine
13
 
14
  class MLProcessor:
15
+ def __init__(self, market_context, data_manager, learning_hub):
16
  self.market_context = market_context
17
  self.data_manager = data_manager
18
+ self.learning_hub = learning_hub
19
 
20
  self.technical_analyzer = AdvancedTechnicalAnalyzer()
21
+ self.strategy_engine = MultiStrategyEngine(data_manager, learning_hub)
 
22
  self.monte_carlo_analyzer = MonteCarloAnalyzer()
23
 
24
+ # استخدام محرك الأنماط المركزي المجهز في DataManager
 
 
25
  if self.data_manager and self.data_manager.pattern_analyzer:
26
+ self.pattern_engine = self.data_manager.pattern_analyzer
 
27
  else:
28
+ self.pattern_engine = None
29
+ print("⚠️ [MLProcessor] تحذير: محرك الأنماط غير متوفر من DataManager.")
 
 
 
30
 
31
  async def process_and_score_symbol_enhanced(self, raw_data, preloaded_whale_data: dict = None):
32
  """
33
+ المعالجة المركزية للعملة: تجمع الأنماط (4 أطر)، المؤشرات، ومونت كارلو المتقدمة.
34
  """
35
  try:
36
+ symbol = raw_data.get('symbol')
37
+ ohlcv_data = raw_data.get('ohlcv') # يجب أن يحتوي على {'15m': [], '1h': [], ...}
 
 
 
 
 
 
38
 
39
+ if not symbol or not ohlcv_data: return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
+ # 1. تجهيز الهيكل الأساسي للنتيجة
42
+ analysis_result = {
43
+ 'symbol': symbol,
44
+ 'current_price': raw_data.get('current_price', 0.0),
45
+ 'layer1_score': raw_data.get('layer1_score', 0.0),
46
+ 'ohlcv': ohlcv_data, # نحتفظ بالبيانات الخام للرجوع إليها
47
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ # 2. تشغيل التحليلات المتقدمة بالتوازي لسرعة الأداء
50
+ # (أنماط XGBoost، مؤشرات فنية، مونت كارلو المتقدمة)
51
+ tasks = [
52
+ self._run_xgboost_patterns(ohlcv_data),
53
+ self._run_advanced_indicators(ohlcv_data),
54
+ self._run_advanced_monte_carlo(ohlcv_data)
55
+ ]
56
+ patterns, indicators, mc_results = await asyncio.gather(*tasks)
57
 
58
+ analysis_result['pattern_analysis'] = patterns
59
+ analysis_result['advanced_indicators'] = indicators
60
+ analysis_result['monte_carlo_distribution'] = mc_results
 
 
 
61
 
62
+ # 3. دمج بيانات الحيتان (إذا توفرت مسبقاً)
63
+ if preloaded_whale_data and symbol in preloaded_whale_data:
64
+ analysis_result['whale_data'] = preloaded_whale_data[symbol]
65
+ else:
66
+ analysis_result['whale_data'] = {'data_available': False}
67
 
68
+ # 4. حساب الدرجة النهائية الموزونة (V11.0 New Weights)
69
+ final_score = self._calculate_final_weighted_score(analysis_result)
70
+ analysis_result['enhanced_final_score'] = final_score
71
 
72
+ return analysis_result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
 
 
 
 
74
  except Exception as e:
75
+ print(f"❌ [Processor] خطأ فادح في معالجة {symbol}: {e}")
76
+ traceback.print_exc()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  return None
78
 
79
+ async def _run_xgboost_patterns(self, ohlcv_data):
80
+ """تشغيل محرك الأنماط الجديد على كافة الأطر الزمنية المتاحة"""
81
+ if not self.pattern_engine or not self.pattern_engine.initialized:
82
+ return {'pattern_confidence': 0.0, 'details': {}}
83
+ try:
84
+ # المحرك الجديد يعيد نتيجة مجمعة جاهزة
85
+ return await self.pattern_engine.detect_chart_patterns(ohlcv_data)
86
+ except Exception as e:
87
+ print(f"⚠️ خطأ في تحليل الأنماط: {e}")
88
+ return {'pattern_confidence': 0.0, 'details': {'error': str(e)}}
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ async def _run_advanced_indicators(self, ohlcv_data):
91
+ """حساب المؤشرات الفنية المتقدمة لأهم الأطر الزمنية"""
92
+ indicators = {}
 
 
 
 
 
 
 
93
  try:
94
+ # نركز على 1h و 4h للتحليل الفني العام
95
+ for tf in ['1h', '4h']:
96
+ if tf in ohlcv_data and len(ohlcv_data[tf]) >= 50:
97
+ df = self._create_dataframe(ohlcv_data[tf])
98
+ indicators[tf] = self.technical_analyzer.calculate_all_indicators(df, tf)
99
+ except Exception: pass
100
+ return indicators
101
+
102
+ async def _run_advanced_monte_carlo(self, ohlcv_data):
103
+ """تشغيل محاكاة مونت كارلو المتقدمة (GARCH+LGBM) على إطار الساعة"""
104
+ try:
105
+ # نستخدم النسخة المتقدمة (المرحلة 2+3) بدلاً من البسيطة
106
+ return await self.monte_carlo_analyzer.generate_1h_distribution_advanced(ohlcv_data)
107
+ except Exception: return None
108
 
109
+ def _calculate_final_weighted_score(self, analysis):
110
+ """
111
+ (V11.0) حساب الدرجة النهائية بالأوزان الجديدة:
112
+ - التحليل الفني: 85% (أنماط 40%، مؤشرات 30%، مونت كارلو 30% من الـ 85%)
113
+ - الحيتان: 10%
114
+ - الأخبار: 5%
115
+ """
116
  try:
117
+ # 1. المكون الفني (Total Technical Score)
118
+ pattern_score = analysis.get('pattern_analysis', {}).get('pattern_confidence', 0.0)
119
+
120
+ # حساب درجة المؤشرات (متوسط بسيط لبعض المؤشرات الرئيسية كمثال)
121
+ inds = analysis.get('advanced_indicators', {}).get('1h', {})
122
+ rsi = inds.get('rsi', 50)
123
+ macd = inds.get('macd_hist', 0)
124
+ ind_score = 0.5
125
+ if rsi > 50: ind_score += 0.1
126
+ if macd > 0: ind_score += 0.1
127
+ # (يمكن تعقيد هذا الجزء أكثر لاحقاً)
128
+
129
+ # درجة مونت كارلو
130
+ mc_prob = analysis.get('monte_carlo_distribution', {}).get('probability_of_gain', 0.5)
131
+ mc_score = max(0.0, min(1.0, (mc_prob - 0.5) * 2.0)) # تطبيع إلى 0-1
132
+
133
+ # دمج المكون الفني (85% من الإجمالي)
134
+ technical_sub_score = (pattern_score * 0.40) + (ind_score * 0.30) + (mc_score * 0.30)
135
+ weighted_technical = technical_sub_score * 0.85
136
+
137
+ # 2. مكون الحيتان (10%)
138
+ whale_score = 0.0
139
+ whale_data = analysis.get('whale_data', {})
140
+ if whale_data.get('data_available'):
141
+ # نفترض وجود درجة ثقة جاهزة من 0 لـ 1
142
+ whale_score = whale_data.get('confidence_score', 0.5)
143
+ weighted_whale = whale_score * 0.10
144
+
145
+ # 3. مكون الأخبار (5%)
146
+ news_score = 0.5 # محايد افتراضياً
147
+ # (سيتم تحديث هذا الجزء لاحقاً عند توفر تحليل الأخبار الحقيقي)
148
+ weighted_news = news_score * 0.05
149
+
150
+ # الدرجة النهائية
151
+ return weighted_technical + weighted_whale + weighted_news
152
+
153
+ except Exception as e:
154
+ # print(f"⚠️ خطأ في حساب الدرجة النهائية: {e}")
155
+ return analysis.get('layer1_score', 0.0) * 0.85 # عودة للدرجة الأولية
156
 
157
+ def _create_dataframe(self, candles):
158
+ if not candles: return pd.DataFrame()
159
+ df = pd.DataFrame(candles, columns=['ts', 'open', 'high', 'low', 'close', 'volume'])
160
+ df[['open', 'high', 'low', 'close', 'volume']] = df[['open', 'high', 'low', 'close', 'volume']].astype(float)
161
+ df['ts'] = pd.to_datetime(df['ts'], unit='ms')
162
+ df.set_index('ts', inplace=True)
163
+ return df
164
+
165
+ print("✅ ML Processor loaded - V11.0 (XGBoost Multi-TF & New Weights)")