Riy777 commited on
Commit
a01528e
·
verified ·
1 Parent(s): 707ad82

Upload 6 files

Browse files
Files changed (6) hide show
  1. LLM (31).py +1051 -0
  2. ML (31).py +1402 -0
  3. app (32).py +953 -0
  4. data_manager (23).py +1431 -0
  5. learning_engine (19).py +644 -0
  6. r2 (29).py +648 -0
LLM (31).py ADDED
@@ -0,0 +1,1051 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, traceback, asyncio, json, re, ast
2
+ from datetime import datetime, timedelta
3
+ from functools import wraps
4
+ from backoff import on_exception, expo
5
+ from openai import OpenAI, RateLimitError, APITimeoutError, APIStatusError
6
+ import numpy as np, httpx, pandas as pd
7
+ from gnews import GNews
8
+ import feedparser
9
+
10
+ NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
11
+ PRIMARY_MODEL = "nvidia/llama-3.1-nemotron-ultra-253b-v1"
12
+ NVIDIA_RATE_LIMIT_CALLS = 20
13
+ NVIDIA_RATE_LIMIT_PERIOD = 60
14
+
15
+ CRYPTO_RSS_FEEDS = {
16
+ "Cointelegraph": "https://cointelegraph.com/rss",
17
+ "CoinDesk": "https://www.coindesk.com/arc/outboundfeeds/rss/",
18
+ "CryptoSlate": "https://cryptoslate.com/feed/",
19
+ "NewsBTC": "https://www.newsbtc.com/feed/",
20
+ "Bitcoin.com": "https://news.bitcoin.com/feed/"
21
+ }
22
+
23
+ class NewsFetcher:
24
+ def __init__(self):
25
+ self.http_client = httpx.AsyncClient(
26
+ timeout=10.0, follow_redirects=True,
27
+ headers={
28
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
29
+ 'Accept': 'application/json, text/plain, */*',
30
+ 'Accept-Language': 'en-US,en;q=0.9',
31
+ 'Cache-Control': 'no-cache'
32
+ }
33
+ )
34
+ self.gnews = GNews(language='en', country='US', period='3h', max_results=8)
35
+
36
+ async def _fetch_from_gnews(self, symbol: str) -> list:
37
+ try:
38
+ base_symbol = symbol.split("/")[0]
39
+ query = f'"{base_symbol}" cryptocurrency -bitcoin -ethereum -BTC -ETH'
40
+ print(f"📰 Fetching specific news from GNews for {base_symbol}...")
41
+ news_items = await asyncio.to_thread(self.gnews.get_news, query)
42
+ print(f"✅ GNews fetched {len(news_items)} specific items for {base_symbol}.")
43
+ return news_items
44
+ except Exception as e:
45
+ print(f"❌ Failed to fetch specific news from GNews for {symbol}: {e}")
46
+ return []
47
+
48
+ async def _fetch_from_rss_feed(self, feed_url: str, source_name: str, symbol: str) -> list:
49
+ try:
50
+ base_symbol = symbol.split('/')[0]
51
+ print(f"📰 Fetching specific news from {source_name} RSS for {base_symbol}...")
52
+ max_redirects = 2
53
+ current_url = feed_url
54
+ for attempt in range(max_redirects):
55
+ try:
56
+ response = await self.http_client.get(current_url)
57
+ response.raise_for_status()
58
+ break
59
+ except httpx.HTTPStatusError as e:
60
+ if e.response.status_code in [301, 302, 307, 308] and 'Location' in e.response.headers:
61
+ current_url = e.response.headers['Location']
62
+ print(f"🔄 Following redirect to: {current_url}")
63
+ continue
64
+ else:
65
+ raise
66
+ feed = feedparser.parse(response.text)
67
+ news_items = []
68
+ search_term = base_symbol.lower()
69
+ for entry in feed.entries[:15]:
70
+ title = entry.title.lower() if hasattr(entry, 'title') else ''
71
+ summary = entry.summary.lower() if hasattr(entry, 'summary') else entry.description.lower() if hasattr(entry, 'description') else ''
72
+ if search_term in title or search_term in summary:
73
+ news_items.append({
74
+ 'title': entry.title,
75
+ 'description': summary,
76
+ 'source': source_name,
77
+ 'published': entry.get('published', '')
78
+ })
79
+ print(f"✅ {source_name} RSS fetched {len(news_items)} specific items for {base_symbol}.")
80
+ return news_items
81
+ except Exception as e:
82
+ print(f"❌ Failed to fetch specific news from {source_name} RSS for {symbol}: {e}")
83
+ return []
84
+
85
+ async def get_news_for_symbol(self, symbol: str) -> str:
86
+ base_symbol = symbol.split("/")[0]
87
+ tasks = [self._fetch_from_gnews(symbol)]
88
+ for name, url in CRYPTO_RSS_FEEDS.items():
89
+ tasks.append(self._fetch_from_rss_feed(url, name, symbol))
90
+
91
+ results = await asyncio.gather(*tasks, return_exceptions=True)
92
+ all_news_text = []
93
+
94
+ for result in results:
95
+ if isinstance(result, Exception):
96
+ print(f"⚠️ A news source failed with error: {result}")
97
+ continue
98
+ for item in result:
99
+ if self._is_directly_relevant_to_symbol(item, base_symbol):
100
+ title = item.get('title', 'No Title')
101
+ description = item.get('description', 'No Description')
102
+ source = item.get('source', 'Unknown Source')
103
+ published = item.get('published', '')
104
+
105
+ news_entry = f"[{source}] {title}. {description}"
106
+ if published:
107
+ news_entry += f" (Published: {published})"
108
+
109
+ all_news_text.append(news_entry)
110
+
111
+ if not all_news_text:
112
+ return f"📰 No specific news found for {base_symbol} in the last 3 hours."
113
+
114
+ important_news = all_news_text[:5]
115
+ return " | ".join(important_news)
116
+
117
+ def _is_directly_relevant_to_symbol(self, news_item, base_symbol):
118
+ title = news_item.get('title', '').lower()
119
+ description = news_item.get('description', '').lower()
120
+ symbol_lower = base_symbol.lower()
121
+
122
+ if symbol_lower not in title and symbol_lower not in description:
123
+ return False
124
+
125
+ crypto_keywords = [
126
+ 'crypto', 'cryptocurrency', 'token', 'blockchain',
127
+ 'price', 'market', 'trading', 'exchange', 'defi',
128
+ 'coin', 'digital currency', 'altcoin'
129
+ ]
130
+
131
+ return any(keyword in title or keyword in description for keyword in crypto_keywords)
132
+
133
+ class PatternAnalysisEngine:
134
+ def __init__(self, llm_service):
135
+ self.llm = llm_service
136
+ self.pattern_templates = {
137
+ 'reversal': ['head_shoulders', 'double_top', 'triple_top', 'rising_wedge', 'falling_wedge'],
138
+ 'continuation': ['flags', 'pennants', 'triangles', 'rectangles', 'cup_and_handle'],
139
+ 'consolidation': ['symmetrical_triangle', 'ascending_triangle', 'descending_triangle']
140
+ }
141
+
142
+ def _format_chart_data_for_llm(self, ohlcv_data):
143
+ """تنسيق بيانات الشموع بشكل محسن للنموذج"""
144
+ if not ohlcv_data or len(ohlcv_data) < 20:
145
+ return "❌ Insufficient chart data for pattern analysis (minimum 20 candles required)"
146
+
147
+ try:
148
+ # استخدام آخر 50 شمعة للتحليل الدقيق
149
+ candles_to_analyze = ohlcv_data[-50:] if len(ohlcv_data) > 50 else ohlcv_data
150
+
151
+ chart_description = [
152
+ "📊 **CANDLE DATA FOR PATTERN ANALYSIS:**",
153
+ f"Total candles available: {len(ohlcv_data)}",
154
+ f"Candles used for analysis: {len(candles_to_analyze)}",
155
+ ""
156
+ ]
157
+
158
+ # إضافة معلومات عن الشموع الرئيسية
159
+ if len(candles_to_analyze) >= 10:
160
+ recent_candles = candles_to_analyze[-10:]
161
+ chart_description.append("**Recent 10 Candles (Latest First):**")
162
+ for i, candle in enumerate(reversed(recent_candles)):
163
+ candle_idx = len(candles_to_analyze) - i
164
+ desc = f"Candle {candle_idx}: O:{candle[1]:.6f} H:{candle[2]:.6f} L:{candle[3]:.6f} C:{candle[4]:.6f} V:{candle[5]:.0f}"
165
+ chart_description.append(f" {desc}")
166
+
167
+ # تحليل الاتجاه العام
168
+ if len(candles_to_analyze) >= 2:
169
+ first_close = candles_to_analyze[0][4]
170
+ last_close = candles_to_analyze[-1][4]
171
+ price_change = ((last_close - first_close) / first_close) * 100
172
+ trend = "📈 BULLISH" if price_change > 2 else "📉 BEARISH" if price_change < -2 else "➡️ SIDEWAYS"
173
+
174
+ # حساب أعلى وأقل سعر
175
+ highs = [c[2] for c in candles_to_analyze]
176
+ lows = [c[3] for c in candles_to_analyze]
177
+ high_max = max(highs)
178
+ low_min = min(lows)
179
+ volatility = ((high_max - low_min) / low_min) * 100
180
+
181
+ chart_description.extend([
182
+ "",
183
+ "**MARKET STRUCTURE ANALYSIS:**",
184
+ f"Trend Direction: {trend}",
185
+ f"Price Change: {price_change:+.2f}%",
186
+ f"Volatility Range: {volatility:.2f}%",
187
+ f"Highest Price: {high_max:.6f}",
188
+ f"Lowest Price: {low_min:.6f}"
189
+ ])
190
+
191
+ # تحليل حجم التداول
192
+ if len(candles_to_analyze) >= 5:
193
+ volumes = [c[5] for c in candles_to_analyze]
194
+ avg_volume = sum(volumes) / len(volumes)
195
+ current_volume = candles_to_analyze[-1][5]
196
+ volume_ratio = current_volume / avg_volume if avg_volume > 0 else 1
197
+
198
+ volume_signal = "🚀 HIGH" if volume_ratio > 2 else "📊 NORMAL" if volume_ratio > 0.5 else "📉 LOW"
199
+ chart_description.extend([
200
+ "",
201
+ "**VOLUME ANALYSIS:**",
202
+ f"Current Volume: {current_volume:,.0f}",
203
+ f"Volume Ratio: {volume_ratio:.2f}x average",
204
+ f"Volume Signal: {volume_signal}"
205
+ ])
206
+
207
+ return "\n".join(chart_description)
208
+
209
+ except Exception as e:
210
+ return f"❌ Error formatting chart data: {str(e)}"
211
+
212
+ async def analyze_chart_patterns(self, symbol, ohlcv_data):
213
+ """تحليل الأنماط البيانية مع تحسينات كبيرة"""
214
+ try:
215
+ if not ohlcv_data or len(ohlcv_data) < 20:
216
+ return {
217
+ "pattern_detected": "insufficient_data",
218
+ "pattern_confidence": 0.1,
219
+ "pattern_strength": "weak",
220
+ "predicted_direction": "unknown",
221
+ "pattern_analysis": "Insufficient candle data for pattern analysis"
222
+ }
223
+
224
+ chart_text = self._format_chart_data_for_llm(ohlcv_data)
225
+
226
+ prompt = f"""
227
+ 🔍 **CRYPTO CHART PATTERN ANALYSIS REQUEST**
228
+
229
+ You are an expert cryptocurrency technical analyst with 10+ years experience.
230
+ Analyze the following candle data for {symbol} and identify STRONG, ACTIONABLE patterns.
231
+
232
+ **ANALYSIS REQUIREMENTS:**
233
+ 1. Focus on CLEAR, HIGH-PROBABILITY patterns only
234
+ 2. Consider volume confirmation for all patterns
235
+ 3. Evaluate pattern strength based on candle formations
236
+ 4. Provide SPECIFIC price targets and stop levels
237
+ 5. Assess timeframe suitability for 5-45 minute trades
238
+
239
+ **CANDLE DATA FOR ANALYSIS:**
240
+ {chart_text}
241
+
242
+ **PATTERNS TO LOOK FOR:**
243
+ 🎯 REVERSAL PATTERNS: Head & Shoulders, Double Top/Bottom, Triple Top/Bottom
244
+ 🎯 CONTINUATION PATTERNS: Flags, Pennants, Triangles, Rectangles
245
+ 🎯 CONSOLIDATION PATTERNS: Symmetrical/Descending/Ascending Triangles
246
+ 🎯 SUPPORT/RESISTANCE: Key levels from recent highs/lows
247
+
248
+ **MANDATORY OUTPUT FORMAT (JSON):**
249
+ {{
250
+ "pattern_detected": "pattern_name",
251
+ "pattern_confidence": 0.85,
252
+ "pattern_strength": "strong/medium/weak",
253
+ "predicted_direction": "up/down/sideways",
254
+ "predicted_movement_percent": 5.50,
255
+ "timeframe_expectation": "15-25 minutes",
256
+ "entry_suggestion": 0.1234,
257
+ "target_suggestion": 0.1357,
258
+ "stop_suggestion": 0.1189,
259
+ "key_support": 0.1200,
260
+ "key_resistance": 0.1300,
261
+ "pattern_analysis": "Detailed explanation of the pattern, why it's valid, and volume confirmation"
262
+ }}
263
+
264
+ **CRITICAL:**
265
+ - Only identify patterns if you have ≥ 70% confidence
266
+ - MUST consider volume in pattern confirmation
267
+ - Provide SPECIFIC numbers for entry/target/stop
268
+ - If no clear pattern, set pattern_detected to "no_clear_pattern"
269
+ """
270
+
271
+ print(f"🔍 Analyzing chart patterns for {symbol} with {len(ohlcv_data)} candles...")
272
+ response = await self.llm._call_llm(prompt)
273
+
274
+ pattern_result = self._parse_pattern_response(response)
275
+ if pattern_result and pattern_result.get('pattern_detected') != 'no_clear_pattern':
276
+ print(f"✅ Pattern detected for {symbol}: {pattern_result.get('pattern_detected')} "
277
+ f"(Confidence: {pattern_result.get('pattern_confidence', 0):.2f})")
278
+ else:
279
+ print(f"ℹ️ No clear patterns for {symbol}")
280
+
281
+ return pattern_result
282
+
283
+ except Exception as e:
284
+ print(f"❌ Chart pattern analysis failed for {symbol}: {e}")
285
+ return None
286
+
287
+ def _parse_pattern_response(self, response_text):
288
+ """تحليل رد النموذج مع تحسينات التعامل مع الأخطاء"""
289
+ try:
290
+ # البحث عن JSON في الرد
291
+ json_match = re.search(r'\{.*\}', response_text, re.DOTALL)
292
+ if not json_match:
293
+ return {
294
+ "pattern_detected": "parse_error",
295
+ "pattern_confidence": 0.1,
296
+ "pattern_analysis": "Could not parse pattern analysis response"
297
+ }
298
+
299
+ pattern_data = json.loads(json_match.group())
300
+
301
+ # التحقق من الحقول الأساسية
302
+ required = ['pattern_detected', 'pattern_confidence', 'predicted_direction']
303
+ if not all(field in pattern_data for field in required):
304
+ return {
305
+ "pattern_detected": "incomplete_data",
306
+ "pattern_confidence": 0.1,
307
+ "pattern_analysis": "Incomplete pattern analysis data"
308
+ }
309
+
310
+ return pattern_data
311
+
312
+ except Exception as e:
313
+ print(f"❌ Error parsing pattern response: {e}")
314
+ return {
315
+ "pattern_detected": "parse_error",
316
+ "pattern_confidence": 0.1,
317
+ "pattern_analysis": f"Error parsing pattern analysis: {str(e)}"
318
+ }
319
+
320
+ class LLMService:
321
+ def __init__(self, api_key=NVIDIA_API_KEY, model_name=PRIMARY_MODEL, temperature=0.7):
322
+ self.api_key = api_key
323
+ self.model_name = model_name
324
+ self.temperature = temperature
325
+ self.client = OpenAI(base_url="https://integrate.api.nvidia.com/v1", api_key=self.api_key)
326
+ self.news_fetcher = NewsFetcher()
327
+ self.pattern_engine = PatternAnalysisEngine(self)
328
+ self.semaphore = asyncio.Semaphore(5)
329
+
330
+ def _rate_limit_nvidia_api(func):
331
+ @wraps(func)
332
+ @on_exception(expo, RateLimitError, max_tries=5)
333
+ async def wrapper(*args, **kwargs):
334
+ return await func(*args, **kwargs)
335
+ return wrapper
336
+
337
+ async def get_trading_decision(self, data_payload: dict):
338
+ try:
339
+ symbol = data_payload.get('symbol', 'unknown')
340
+ target_strategy = data_payload.get('target_strategy', 'GENERIC')
341
+ print(f"🧠 Starting LLM analysis for {symbol} with strategy: {target_strategy}...")
342
+
343
+ news_text = await self.news_fetcher.get_news_for_symbol(symbol)
344
+ pattern_analysis = await self._get_pattern_analysis(data_payload)
345
+ prompt = self._create_enhanced_trading_prompt(data_payload, news_text, pattern_analysis)
346
+
347
+ print(f"🧠 Sending enhanced prompt to LLM for {symbol}...")
348
+ async with self.semaphore:
349
+ response = await self._call_llm(prompt)
350
+
351
+ decision_dict = self._parse_llm_response_enhanced(response, target_strategy, symbol)
352
+ if decision_dict:
353
+ decision_dict['model_source'] = self.model_name
354
+ decision_dict['pattern_analysis'] = pattern_analysis
355
+
356
+ # ✅ التحقق النهائي من الاستراتيجية
357
+ final_strategy = decision_dict.get('strategy')
358
+ if not final_strategy or final_strategy == 'unknown' or final_strategy is None:
359
+ decision_dict['strategy'] = target_strategy
360
+ print(f"🔧 Final strategy correction for {symbol}: {target_strategy}")
361
+ else:
362
+ print(f"✅ LLM successfully selected strategy '{final_strategy}' for {symbol}.")
363
+
364
+ print(f"✅ LLM analysis completed for {symbol} - Strategy: {decision_dict['strategy']}")
365
+ else:
366
+ print(f"❌ LLM analysis failed for {symbol}")
367
+ return local_analyze_opportunity(data_payload)
368
+
369
+ return decision_dict
370
+
371
+ except Exception as e:
372
+ print(f"❌ An error occurred while getting LLM decision for {data_payload.get('symbol', 'unknown')}: {e}")
373
+ traceback.print_exc()
374
+ return local_analyze_opportunity(data_payload)
375
+
376
+ def _parse_llm_response_enhanced(self, response_text: str, fallback_strategy: str = 'GENERIC', symbol: str = 'unknown') -> dict:
377
+ """✅ الإصلاح النهائي: تحليل رد الـ LLM مع إعطاء الثقة لقراره"""
378
+ try:
379
+ json_match = re.search(r'```json\n(.*?)\n```', response_text, re.DOTALL)
380
+ if json_match:
381
+ json_str = json_match.group(1).strip()
382
+ else:
383
+ json_match = re.search(r'\{.*\}', response_text, re.DOTALL)
384
+ if json_match:
385
+ json_str = json_match.group()
386
+ else:
387
+ print(f"❌ No JSON found in LLM response for {symbol}: {response_text}")
388
+ return None
389
+
390
+ decision_data = json.loads(json_str)
391
+
392
+ required_fields = ['action', 'reasoning', 'risk_assessment', 'trade_type',
393
+ 'stop_loss', 'take_profit', 'expected_target_minutes', 'confidence_level']
394
+
395
+ for field in required_fields:
396
+ if field not in decision_data:
397
+ print(f"❌ Missing required field '{field}' in LLM response for {symbol}")
398
+ return None
399
+
400
+ strategy_value = decision_data.get('strategy')
401
+ # 💡 التحقق: هل الاستراتيجية التي أرجعها النموذج صالحة؟
402
+ if not strategy_value or strategy_value == 'unknown' or strategy_value is None:
403
+ # إذا كانت غير صالحة، استخدم الاستراتيجية العامة كخطة بديلة آمنة
404
+ print(f"⚠️ LLM returned invalid strategy '{strategy_value}' for {symbol}. Forcing fallback: {fallback_strategy}")
405
+ decision_data['strategy'] = fallback_strategy
406
+ else:
407
+ # إذا كانت صالحة، اعتمدها مباشرةً!
408
+ print(f"✅ LLM successfully selected strategy '{strategy_value}' for {symbol}.")
409
+
410
+ return decision_data
411
+
412
+ except Exception as e:
413
+ print(f"❌ Unexpected error parsing LLM response for {symbol}: {e}")
414
+ return None
415
+
416
+ async def _get_pattern_analysis(self, data_payload):
417
+ try:
418
+ symbol = data_payload['symbol']
419
+ # ✅ الحصول على بيانات الشموع الخام من البيانات المعالجة
420
+ if 'raw_ohlcv' in data_payload and '1h' in data_payload['raw_ohlcv']:
421
+ ohlcv_data = data_payload['raw_ohlcv']['1h']
422
+ if ohlcv_data and len(ohlcv_data) >= 20:
423
+ print(f"🔍 Using raw OHLCV data for pattern analysis: {len(ohlcv_data)} candles")
424
+ return await self.pattern_engine.analyze_chart_patterns(symbol, ohlcv_data)
425
+
426
+ # ✅ الحصول على بيانات OHLCV من 'advanced_indicators' كبديل
427
+ if 'advanced_indicators' in data_payload and '1h' in data_payload['advanced_indicators']:
428
+ ohlcv_data = data_payload['advanced_indicators']['1h']
429
+ if ohlcv_data and len(ohlcv_data) >= 20:
430
+ print(f"🔍 Using advanced indicators data for pattern analysis: {len(ohlcv_data)} candles")
431
+ return await self.pattern_engine.analyze_chart_patterns(symbol, ohlcv_data)
432
+
433
+ print(f"⚠️ No sufficient OHLCV data for pattern analysis on {symbol}")
434
+ return None
435
+ except Exception as e:
436
+ print(f"⚠️ Pattern analysis failed for {data_payload.get('symbol')}: {e}")
437
+ return None
438
+
439
+ def _create_enhanced_trading_prompt(self, payload: dict, news_text: str, pattern_analysis: dict) -> str:
440
+ symbol = payload.get('symbol', 'N/A')
441
+ current_price = payload.get('current_price', 'N/A')
442
+ reasons = payload.get('reasons_for_candidacy', [])
443
+ sentiment_data = payload.get('sentiment_data', {})
444
+ advanced_indicators = payload.get('advanced_indicators', {})
445
+ strategy_scores = payload.get('strategy_scores', {})
446
+ recommended_strategy = payload.get('recommended_strategy', 'N/A')
447
+ target_strategy = payload.get('target_strategy', 'GENERIC')
448
+ final_score = payload.get('final_score', 'N/A')
449
+ enhanced_final_score = payload.get('enhanced_final_score', 'N/A')
450
+ whale_data = payload.get('whale_data', {})
451
+
452
+ general_whale_activity = sentiment_data.get('general_whale_activity', {})
453
+
454
+ final_score_display = f"{final_score:.2f}" if isinstance(final_score, (int, float)) else str(final_score)
455
+ enhanced_score_display = f"{enhanced_final_score:.2f}" if isinstance(enhanced_final_score, (int, float)) else str(enhanced_final_score)
456
+
457
+ indicators_summary = self._format_advanced_indicators(advanced_indicators)
458
+ strategies_summary = self._format_strategies_analysis(strategy_scores, recommended_strategy)
459
+ pattern_summary = self._format_pattern_analysis_enhanced(pattern_analysis, payload)
460
+ whale_analysis_section = self._format_whale_analysis_for_llm(general_whale_activity, whale_data, symbol)
461
+
462
+ strategy_instructions = {
463
+ "AGGRESSIVE_GROWTH": "**Strategy: AGGRESSIVE_GROWTH**: Focus on strong price movements (5-10%) and accept higher risk for higher rewards. Aim for 8-15% on successful trades.",
464
+ "DEFENSIVE_GROWTH": "**Strategy: DEFENSIVE_GROWTH**: Look for safer 3-6% moves with tight stop-losses. Aim for 4-8% while protecting capital.",
465
+ "CONSERVATIVE": "**Strategy: CONSERVATIVE**: Focus on only 2-4% moves with wider stop-losses. Aim for 2-5% with minimal risk.",
466
+ "HIGH_FREQUENCY": "**Strategy: HIGH_FREQUENCY**: Look for quick 1-3% scalps with very tight stop-losses. Aim for 1-4% on multiple trades.",
467
+ "WHALE_FOLLOWING": "**Strategy: WHALE_FOLLOWING**: Prioritize whale tracking signals and unusual volume. Aim for 5-12% with medium risk.",
468
+ "GENERIC": "**Strategy: GENERIC**: Make balanced decisions considering risk and reward across all factors."
469
+ }
470
+ strategy_instruction = strategy_instructions.get(target_strategy, strategy_instructions["GENERIC"])
471
+
472
+ data_availability_section = self._format_data_availability(sentiment_data, whale_data, news_text, pattern_analysis)
473
+
474
+ prompt = f"""
475
+ 🎯 **ENHANCED TRADING ANALYSIS WITH CHART PATTERNS**
476
+
477
+ **ACTIVE STRATEGY: {target_strategy}**
478
+ {strategy_instruction}
479
+
480
+ **CRITICAL CHART PATTERN ANALYSIS:**
481
+ {pattern_summary}
482
+
483
+ **STRATEGIC TIMEFRAME:**
484
+ - Max trade duration: 45 minutes (will be automatically enforced).
485
+ - Optimal range: 8-25 minutes for ideal capital rotation.
486
+ - Minimum duration: 5 minutes for active monitoring.
487
+
488
+ {data_availability_section}
489
+
490
+ **AVAILABLE DATA FOR {symbol}:**
491
+
492
+ **1. 🎯 CANDIDACY REASON:**
493
+ - This symbol was selected for: {reasons}
494
+
495
+ **2. 📊 OVERVIEW:**
496
+ - Symbol: {symbol}
497
+ - Current Price: {current_price} USDT
498
+ - Initial System Score: {final_score_display}
499
+ - Enhanced System Score: {enhanced_score_display}
500
+ - Recommended Internal Strategy: {recommended_strategy}
501
+ - **Target Trading Strategy: {target_strategy}**
502
+
503
+ **3. 🎪 STRATEGY ANALYSIS (INTERNAL SCORES):**
504
+ {strategies_summary}
505
+
506
+ **4. 📈 ADVANCED TECHNICAL INDICATORS:**
507
+ {indicators_summary}
508
+
509
+ **5. 🌍 COMPREHENSIVE MARKET CONTEXT:**
510
+ - BTC Trend: {sentiment_data.get('btc_sentiment', 'N/A')}
511
+ - Fear & Greed Index: {sentiment_data.get('fear_and_greed_index', 'N/A')} ({sentiment_data.get('sentiment_class', 'N/A')})
512
+ - Market Regime: {sentiment_data.get('market_trend', 'N/A')}
513
+
514
+ **6. 🐋 ADVANCED WHALE ANALYSIS:**
515
+ {whale_analysis_section}
516
+
517
+ **7. 📰 RECENT NEWS (LAST 3 HOURS):**
518
+ {news_text}
519
+
520
+ **YOUR MISSION:**
521
+ Integrate the chart pattern analysis above with all other available data to make a FINAL trading decision.
522
+
523
+ **IF PATTERN ANALYSIS SHOWS STRONG SIGNALS:**
524
+ - Give it significant weight in your decision
525
+ - Use the pattern's entry/target/stop suggestions
526
+ - Consider the pattern's confidence level
527
+
528
+ **IF NO CLEAR PATTERNS:**
529
+ - Rely more on technical indicators and market context
530
+ - Be more conservative with targets and stops
531
+
532
+ **REQUIRED OUTPUTS (JSON ONLY):**
533
+ - `action`: Must be one of ("BUY", "SELL", "HOLD")
534
+ - `reasoning`: Detailed explanation focusing on {target_strategy} AND SPECIFICALLY MENTIONING chart pattern analysis
535
+ - `risk_assessment`: Risk analysis aligned with {target_strategy} and available data
536
+ - `trade_type`: ("LONG" for BUY, "SHORT" for SELL)
537
+ - `stop_loss`: Stop loss price (consider {target_strategy} risk profile AND pattern suggestions)
538
+ - `take_profit`: Take profit price (realistic for {target_strategy} AND pattern targets)
539
+ - `expected_target_minutes`: Realistic expectation (5-45 minutes)
540
+ - `confidence_level`: Your confidence level (0.00-1.00) based on data quality AND pattern confidence
541
+ - `strategy`: "{target_strategy}" # ⚠️ MUST BE EXACTLY: {target_strategy}
542
+ - `pattern_influence`: "Describe how chart pattern affected decision"
543
+
544
+ **CRITICAL: You MUST include the 'strategy' field with the exact value: "{target_strategy}"**
545
+
546
+ **SPECIAL INSTRUCTIONS FOR PATTERN INTEGRATION:**
547
+ - If pattern_confidence > 0.7, you MUST reference it prominently in reasoning
548
+ - If pattern suggests specific levels, strongly consider using them
549
+ - Always explain how patterns influenced your final decision in 'pattern_influence'
550
+
551
+ **Example output format (JSON only):**
552
+ ```json
553
+ {{
554
+ "action": "BUY",
555
+ "reasoning": "Strong bullish signals aligned with {target_strategy}. High-confidence Double Top pattern detected with 85% confidence suggesting upward movement. Whale activity is positive. Limited news data, but technicals and pattern are strong.",
556
+ "risk_assessment": "Moderate risk suitable for {target_strategy}. Pattern provides clear stop and target levels. Note: Some data sources unavailable.",
557
+ "trade_type": "LONG",
558
+ "stop_loss": 0.0285,
559
+ "take_profit": 0.0320,
560
+ "expected_target_minutes": 12,
561
+ "confidence_level": 0.82,
562
+ "strategy": "{target_strategy}",
563
+ "pattern_influence": "Double Top pattern provided clear entry and target levels, increasing confidence in the trade setup."
564
+ }}
565
+ ```
566
+ """
567
+ return prompt
568
+
569
+ def _format_data_availability(self, sentiment_data, whale_data, news_text, pattern_analysis):
570
+ general_whale_available = sentiment_data.get('general_whale_activity', {}).get('data_available', False)
571
+ symbol_whale_available = whale_data.get('data_available', False)
572
+ news_available = "No specific news found" not in news_text
573
+ pattern_available = pattern_analysis is not None and pattern_analysis.get('pattern_detected') != 'no_clear_pattern'
574
+
575
+ return f"""
576
+ **📊 REAL DATA AVAILABILITY STATUS:**
577
+ - Market Sentiment: {'✅ Available' if sentiment_data.get('fear_and_greed_index') else '❌ Not Available'}
578
+ - General Whale Activity: {'✅ Available' if general_whale_available else '❌ Not Available'}
579
+ - Symbol Whale Activity: {'✅ Available' if symbol_whale_available else '❌ Not Available'}
580
+ - News Data: {'✅ Available' if news_available else '❌ Not Available'}
581
+ - Chart Patterns: {'✅ STRONG PATTERN' if pattern_available and pattern_analysis.get('pattern_confidence', 0) > 0.7 else '✅ WEAK PATTERN' if pattern_available else '❌ Not Available'}
582
+
583
+ **⚠️ IMPORTANT: Decisions should be based ONLY on available real data.**
584
+ **🎯 PATTERN PRIORITY: Give significant weight to chart patterns when available with high confidence.**
585
+ """
586
+
587
+ def _format_advanced_indicators(self, advanced_indicators):
588
+ if not advanced_indicators:
589
+ return "❌ No data for advanced indicators."
590
+
591
+ summary = []
592
+ for timeframe, indicators in advanced_indicators.items():
593
+ if indicators:
594
+ parts = []
595
+ if 'rsi' in indicators: parts.append(f"RSI: {indicators['rsi']:.2f}")
596
+ if 'macd_hist' in indicators: parts.append(f"MACD Hist: {indicators['macd_hist']:.4f}")
597
+ if 'volume_ratio' in indicators: parts.append(f"Volume: {indicators['volume_ratio']:.2f}x")
598
+ if parts:
599
+ summary.append(f"\n📊 **{timeframe}:** {', '.join(parts)}")
600
+
601
+ return "\n".join(summary) if summary else "⚠️ Insufficient indicator data."
602
+
603
+ def _format_strategies_analysis(self, strategy_scores, recommended_strategy):
604
+ if not strategy_scores:
605
+ return "❌ No strategy data available."
606
+
607
+ summary = [f"🎯 **Recommended Strategy:** {recommended_strategy}"]
608
+ sorted_scores = sorted(strategy_scores.items(), key=lambda item: item[1], reverse=True)
609
+ for strategy, score in sorted_scores:
610
+ if isinstance(score, (int, float)):
611
+ score_display = f"{score:.3f}"
612
+ else:
613
+ score_display = str(score)
614
+ summary.append(f" • {strategy}: {score_display}")
615
+
616
+ return "\n".join(summary)
617
+
618
+ def _format_pattern_analysis_enhanced(self, pattern_analysis, payload):
619
+ """تنسيق محسن لقسم تحليل النمط"""
620
+ if not pattern_analysis:
621
+ return """
622
+ ❌ **CHART PATTERN STATUS: NO CLEAR PATTERNS DETECTED**
623
+ - Reason: Insufficient data or no recognizable patterns in current chart
624
+ - Impact: Decision will rely more on technical indicators and market context
625
+ - Recommendation: Proceed with caution, use wider stops
626
+ """
627
+
628
+ confidence = pattern_analysis.get('pattern_confidence', 0)
629
+ pattern_name = pattern_analysis.get('pattern_detected', 'unknown')
630
+ strength = pattern_analysis.get('pattern_strength', 'unknown')
631
+
632
+ if confidence >= 0.7:
633
+ status = "✅ **HIGH-CONFIDENCE PATTERN DETECTED**"
634
+ influence = "This pattern should SIGNIFICANTLY influence your trading decision"
635
+ elif confidence >= 0.5:
636
+ status = "⚠️ **MEDIUM-CONFIDENCE PATTERN DETECTED**"
637
+ influence = "Consider this pattern but verify with other indicators"
638
+ else:
639
+ status = "📊 **LOW-CONFIDENCE PATTERN DETECTED**"
640
+ influence = "Use this pattern as supplementary information only"
641
+
642
+ analysis_lines = [
643
+ status,
644
+ f"**Pattern:** {pattern_name}",
645
+ f"**Confidence:** {confidence:.1%}",
646
+ f"**Strength:** {strength}",
647
+ f"**Predicted Move:** {pattern_analysis.get('predicted_direction', 'N/A')} "
648
+ f"by {pattern_analysis.get('predicted_movement_percent', 0):.2f}%",
649
+ f"**Timeframe:** {pattern_analysis.get('timeframe_expectation', 'N/A')}",
650
+ f"**Influence:** {influence}",
651
+ "",
652
+ "**PATTERN-SPECIFIC SUGGESTIONS:**",
653
+ f"Entry: {pattern_analysis.get('entry_suggestion', 'N/A')}",
654
+ f"Target: {pattern_analysis.get('target_suggestion', 'N/A')}",
655
+ f"Stop: {pattern_analysis.get('stop_suggestion', 'N/A')}",
656
+ f"Key Support: {pattern_analysis.get('key_support', 'N/A')}",
657
+ f"Key Resistance: {pattern_analysis.get('key_resistance', 'N/A')}",
658
+ "",
659
+ f"**Analysis:** {pattern_analysis.get('pattern_analysis', 'No detailed analysis available')}"
660
+ ]
661
+
662
+ return "\n".join(analysis_lines)
663
+
664
+ def _format_whale_analysis_for_llm(self, general_whale_activity, symbol_whale_data, symbol):
665
+ analysis_parts = []
666
+
667
+ if general_whale_activity.get('data_available', False):
668
+ critical_flag = " 🚨 CRITICAL ALERT" if general_whale_activity.get('critical_alert') else ""
669
+ analysis_parts.append(f"📊 **General Market:** {general_whale_activity.get('description', 'Activity detected')}{critical_flag}")
670
+ else:
671
+ analysis_parts.append("📊 **General Market:** No significant general whale data available")
672
+
673
+ if symbol_whale_data.get('data_available', False):
674
+ analysis_parts.append(f"🎯 **{symbol} Specific:** {symbol_whale_data.get('description', 'Activity detected')}")
675
+ else:
676
+ analysis_parts.append(f"🎯 **{symbol} Specific:** No contract-based whale data available")
677
+
678
+ return "\n".join(analysis_parts)
679
+
680
+ async def re_analyze_trade_async(self, trade_data: dict, processed_data: dict):
681
+ try:
682
+ symbol = trade_data['symbol']
683
+ original_strategy = trade_data.get('strategy', 'GENERIC')
684
+
685
+ if not original_strategy or original_strategy == 'unknown':
686
+ original_strategy = trade_data.get('decision_data', {}).get('strategy', 'GENERIC')
687
+ print(f"🔧 Fixed missing original strategy for {symbol}: {original_strategy}")
688
+
689
+ print(f"🧠 Starting LLM re-analysis for {symbol} with strategy: {original_strategy}...")
690
+
691
+ news_text = await self.news_fetcher.get_news_for_symbol(symbol)
692
+ pattern_analysis = await self._get_pattern_analysis(processed_data)
693
+ prompt = self._create_enhanced_re_analysis_prompt(trade_data, processed_data, news_text, pattern_analysis)
694
+
695
+ async with self.semaphore:
696
+ response = await self._call_llm(prompt)
697
+
698
+ re_analysis_dict = self._parse_re_analysis_response_enhanced(response, original_strategy, symbol)
699
+ if re_analysis_dict:
700
+ re_analysis_dict['model_source'] = self.model_name
701
+
702
+ final_strategy = re_analysis_dict.get('strategy')
703
+ if not final_strategy or final_strategy == 'unknown':
704
+ re_analysis_dict['strategy'] = original_strategy
705
+ print(f"🔧 Final re-analysis strategy correction for {symbol}: {original_strategy}")
706
+ else:
707
+ print(f"✅ LLM re-analysis confirmed strategy '{final_strategy}' for {symbol}.")
708
+
709
+ print(f"✅ LLM re-analysis completed for {symbol} - Strategy: {re_analysis_dict['strategy']}")
710
+ else:
711
+ print(f"❌ LLM re-analysis failed for {symbol}")
712
+ return local_re_analyze_trade(trade_data, processed_data)
713
+
714
+ return re_analysis_dict
715
+
716
+ except Exception as e:
717
+ print(f"❌ Unexpected error in enhanced LLM re-analysis: {e}")
718
+ return local_re_analyze_trade(trade_data, processed_data)
719
+
720
+ def _parse_re_analysis_response_enhanced(self, response_text: str, fallback_strategy: str = 'GENERIC', symbol: str = 'unknown') -> dict:
721
+ """✅ الإصلاح النهائي: تحليل رد إعادة التحليل مع إعطاء الثقة لقراره"""
722
+ try:
723
+ json_match = re.search(r'```json\n(.*?)\n```', response_text, re.DOTALL)
724
+ if json_match:
725
+ json_str = json_match.group(1).strip()
726
+ else:
727
+ json_match = re.search(r'\{.*\}', response_text, re.DOTALL)
728
+ if json_match:
729
+ json_str = json_match.group()
730
+ else:
731
+ print(f"❌ No JSON found in re-analysis response for {symbol}: {response_text}")
732
+ return None
733
+
734
+ decision_data = json.loads(json_str)
735
+
736
+ strategy_value = decision_data.get('strategy')
737
+ # 💡 التحقق: هل الاستراتيجية التي أرجعها النموذج صالحة؟
738
+ if not strategy_value or strategy_value == 'unknown' or strategy_value is None:
739
+ # إذا كانت غير صالحة، استخدم الاستراتيجية الأصلية كخطة بديلة آمنة
740
+ print(f"⚠️ LLM re-analysis returned invalid strategy '{strategy_value}' for {symbol}. Forcing fallback: {fallback_strategy}")
741
+ decision_data['strategy'] = fallback_strategy
742
+ else:
743
+ # إذا كانت صالحة، اعتمدها مباشرةً!
744
+ print(f"✅ LLM re-analysis confirmed strategy '{strategy_value}' for {symbol}.")
745
+
746
+ return decision_data
747
+
748
+ except Exception as e:
749
+ print(f"❌ Unexpected error parsing re-analysis response for {symbol}: {e}")
750
+ return None
751
+
752
+ def _create_enhanced_re_analysis_prompt(self, trade_data: dict, processed_data: dict, news_text: str, pattern_analysis: dict) -> str:
753
+ symbol = trade_data.get('symbol', 'N/A')
754
+ entry_price = trade_data.get('entry_price', 'N/A')
755
+ current_price = processed_data.get('current_price', 'N/A')
756
+ strategy = trade_data.get('strategy', 'GENERIC')
757
+
758
+ if not strategy or strategy == 'unknown':
759
+ strategy = 'GENERIC'
760
+
761
+ try:
762
+ price_change = ((current_price - entry_price) / entry_price) * 100
763
+ performance_status = "Profit" if price_change > 0 else "Loss"
764
+ price_change_display = f"{price_change:+.2f}%"
765
+ except (TypeError, ZeroDivisionError):
766
+ price_change_display = "N/A"
767
+ performance_status = "Unknown"
768
+
769
+ indicators_summary = self._format_advanced_indicators(processed_data.get('advanced_indicators', {}))
770
+ pattern_summary = self._format_pattern_analysis_enhanced(pattern_analysis, processed_data)
771
+ whale_analysis_section = self._format_whale_analysis_for_llm(processed_data.get('sentiment_data', {}).get('general_whale_activity', {}), processed_data.get('whale_data', {}), symbol)
772
+
773
+ prompt = f"""
774
+ 🔄 **ENHANCED TRADE RE-ANALYSIS WITH CHART PATTERNS**
775
+
776
+ You are re-analyzing an open trade with new market data and chart patterns.
777
+
778
+ **TRADE CONTEXT ({strategy} STRATEGY):**
779
+ - Original Strategy: {strategy}
780
+ - Symbol: {symbol}
781
+ - Entry Price: {entry_price} USDT
782
+ - Current Price: {current_price} USDT
783
+ - Current Performance: {price_change_display} ({performance_status})
784
+ - Original Strategy: {strategy}
785
+
786
+ **UPDATED CHART PATTERN ANALYSIS:**
787
+ {pattern_summary}
788
+
789
+ **NEW MARKET DATA:**
790
+ - Updated Technicals: {indicators_summary}
791
+ - Updated Whale Intel: {whale_analysis_section}
792
+ - Latest News: {news_text}
793
+
794
+ **DECISION STRATEGY FOR {strategy}:**
795
+ - If pattern shows MORE profit potential: UPDATE with new targets and time
796
+ - If pattern suggests WEAKNESS: CLOSE immediately
797
+ - If pattern still VALID but needs more time: UPDATE with extended timing
798
+ - If pattern INVALIDATED: CLOSE to protect capital
799
+
800
+ **PATTERN-BASED DECISION GUIDELINES:**
801
+ - High-confidence patterns (>70%): Give them primary decision weight
802
+ - Medium-confidence patterns (50-70%): Use as supporting evidence
803
+ - Low-confidence patterns (<50%): Use cautiously with other factors
804
+
805
+ **REQUIRED OUTPUTS (JSON ONLY):**
806
+ - `action`: Must be ("HOLD", "CLOSE_TRADE", "UPDATE_TRADE")
807
+ - `reasoning`: Justification based on new data AND pattern analysis
808
+ - `new_stop_loss`: New stop loss if updating (consider pattern levels)
809
+ - `new_take_profit`: New take profit if updating (consider pattern targets)
810
+ - `new_expected_minutes`: New expected time if updating (null otherwise)
811
+ - `confidence_level`: Confidence in this decision (0.00-1.00)
812
+ - `strategy`: "{strategy}" # ⚠️ MUST BE EXACTLY: {strategy}
813
+ - `pattern_influence_reanalysis`: "Describe how updated pattern analysis affected decision"
814
+
815
+ **CRITICAL: You MUST include the 'strategy' field with the exact value: "{strategy}"**
816
+ """
817
+ return prompt
818
+
819
+ @_rate_limit_nvidia_api
820
+ async def _call_llm(self, prompt: str) -> str:
821
+ try:
822
+ response = self.client.chat.completions.create(
823
+ model=self.model_name,
824
+ messages=[{"role": "user", "content": prompt}],
825
+ temperature=self.temperature,
826
+ seed=42
827
+ )
828
+ return response.choices[0].message.content
829
+ except (RateLimitError, APITimeoutError) as e:
830
+ print(f"❌ LLM API Error: {e}. Retrying...")
831
+ raise
832
+ except Exception as e:
833
+ print(f"❌ Unexpected LLM API error: {e}")
834
+ raise
835
+
836
+ # نظام تتبع أداء الأنماط
837
+ class PatternPerformanceTracker:
838
+ def __init__(self):
839
+ self.pattern_success_rates = {}
840
+ self.pattern_history = []
841
+
842
+ async def track_pattern_performance(self, trade_data, pattern_analysis, outcome, profit_percent):
843
+ """تتبع أداء الأنماط المختلفة"""
844
+ pattern_name = pattern_analysis.get('pattern_detected', 'unknown')
845
+ confidence = pattern_analysis.get('pattern_confidence', 0)
846
+
847
+ if pattern_name not in self.pattern_success_rates:
848
+ self.pattern_success_rates[pattern_name] = {
849
+ 'success_count': 0,
850
+ 'total_count': 0,
851
+ 'total_profit': 0,
852
+ 'avg_profit': 0,
853
+ 'confidence_sum': 0,
854
+ 'avg_confidence': 0
855
+ }
856
+
857
+ stats = self.pattern_success_rates[pattern_name]
858
+ stats['total_count'] += 1
859
+ stats['confidence_sum'] += confidence
860
+
861
+ success = outcome in ["SUCCESS", "CLOSED_BY_REANALYSIS", "CLOSED_BY_MONITOR"] and profit_percent > 0
862
+ if success:
863
+ stats['success_count'] += 1
864
+ stats['total_profit'] += profit_percent
865
+ stats['avg_profit'] = stats['total_profit'] / stats['success_count']
866
+
867
+ stats['avg_confidence'] = stats['confidence_sum'] / stats['total_count']
868
+
869
+ success_rate = stats['success_count'] / stats['total_count']
870
+
871
+ # تسجيل التاريخ
872
+ self.pattern_history.append({
873
+ 'timestamp': datetime.now().isoformat(),
874
+ 'pattern': pattern_name,
875
+ 'confidence': confidence,
876
+ 'success': success,
877
+ 'profit_percent': profit_percent,
878
+ 'symbol': trade_data.get('symbol', 'unknown')
879
+ })
880
+
881
+ print(f"📊 Pattern {pattern_name}: Success Rate {success_rate:.1%}, Avg Profit: {stats['avg_profit']:.2f}%, Avg Confidence: {stats['avg_confidence']:.1%}")
882
+
883
+ return success_rate
884
+
885
+ def get_pattern_recommendations(self):
886
+ """الحصول على توصيات بناءً على أداء الأنماط"""
887
+ recommendations = []
888
+
889
+ for pattern, stats in self.pattern_success_rates.items():
890
+ if stats['total_count'] >= 3: # على الأقل 3 صفقات لتكوين توصية
891
+ success_rate = stats['success_count'] / stats['total_count']
892
+
893
+ if success_rate > 0.7:
894
+ recommendations.append(f"✅ **{pattern}**: Excellent performance ({success_rate:.1%} success) - Prioritize this pattern")
895
+ elif success_rate > 0.5:
896
+ recommendations.append(f"⚠️ **{pattern}**: Good performance ({success_rate:.1%} success) - Use with confidence")
897
+ elif success_rate < 0.3:
898
+ recommendations.append(f"❌ **{pattern}**: Poor performance ({success_rate:.1%} success) - Use cautiously")
899
+
900
+ return recommendations
901
+
902
+ # إنشاء نسخة عالمية من متتبع الأداء
903
+ pattern_tracker_global = PatternPerformanceTracker()
904
+
905
+ def local_analyze_opportunity(candidate_data):
906
+ """تحليل محسن مع مراعاة مخاطر RSI"""
907
+ score = candidate_data.get('enhanced_final_score', candidate_data.get('final_score', 0))
908
+ quality_warnings = candidate_data.get('quality_warnings', [])
909
+
910
+ strategy = candidate_data.get('target_strategy', 'GENERIC')
911
+
912
+ rsi_critical = any('🚨 RSI CRITICAL' in warning for warning in quality_warnings)
913
+ rsi_warning = any('⚠️ RSI WARNING' in warning for warning in quality_warnings)
914
+
915
+ if rsi_critical:
916
+ return {
917
+ "action": "HOLD",
918
+ "reasoning": "Local analysis: CRITICAL RSI levels detected - extreme overbought condition. High risk of correction.",
919
+ "trade_type": "NONE",
920
+ "stop_loss": None,
921
+ "take_profit": None,
922
+ "expected_target_minutes": 15,
923
+ "confidence_level": 0.1,
924
+ "model_source": "local_safety_filter",
925
+ "strategy": strategy
926
+ }
927
+
928
+ advanced_indicators = candidate_data.get('advanced_indicators', {})
929
+ strategy_scores = candidate_data.get('strategy_scores', {})
930
+
931
+ if not advanced_indicators:
932
+ return {
933
+ "action": "HOLD",
934
+ "reasoning": "Local analysis: Insufficient advanced indicator data.",
935
+ "trade_type": "NONE",
936
+ "stop_loss": None,
937
+ "take_profit": None,
938
+ "expected_target_minutes": 15,
939
+ "confidence_level": 0.3,
940
+ "model_source": "local",
941
+ "strategy": strategy
942
+ }
943
+
944
+ action = "HOLD"
945
+ reasoning = "Local analysis: No strong buy signal based on enhanced rules."
946
+ trade_type = "NONE"
947
+ stop_loss = None
948
+ take_profit = None
949
+ expected_minutes = 15
950
+ confidence = 0.3
951
+
952
+ five_minute_indicators = advanced_indicators.get('5m', {})
953
+ one_hour_indicators = advanced_indicators.get('1h', {})
954
+
955
+ buy_conditions = 0
956
+ total_conditions = 0
957
+
958
+ if isinstance(score, (int, float)) and score > 0.70:
959
+ buy_conditions += 1
960
+ total_conditions += 1
961
+
962
+ rsi_five_minute = five_minute_indicators.get('rsi', 50)
963
+ if 30 <= rsi_five_minute <= 65:
964
+ buy_conditions += 1
965
+ total_conditions += 1
966
+
967
+ if five_minute_indicators.get('macd_hist', 0) > 0:
968
+ buy_conditions += 1
969
+ total_conditions += 1
970
+
971
+ if (five_minute_indicators.get('ema_9', 0) > five_minute_indicators.get('ema_21', 0) and
972
+ one_hour_indicators.get('ema_9', 0) > one_hour_indicators.get('ema_21', 0)):
973
+ buy_conditions += 1
974
+ total_conditions += 1
975
+
976
+ if five_minute_indicators.get('volume_ratio', 0) > 1.5:
977
+ buy_conditions += 1
978
+ total_conditions += 1
979
+
980
+ confidence = buy_conditions / total_conditions if total_conditions > 0 else 0.3
981
+
982
+ if rsi_warning:
983
+ confidence *= 0.7
984
+ reasoning += " RSI warning applied."
985
+
986
+ if confidence >= 0.6:
987
+ action = "BUY"
988
+ current_price = candidate_data['current_price']
989
+ trade_type = "LONG"
990
+
991
+ if rsi_warning:
992
+ stop_loss = current_price * 0.93
993
+ else:
994
+ stop_loss = current_price * 0.95
995
+
996
+ if 'bb_upper' in five_minute_indicators:
997
+ take_profit = five_minute_indicators['bb_upper'] * 1.02
998
+ else:
999
+ take_profit = current_price * 1.05
1000
+
1001
+ if confidence >= 0.8:
1002
+ expected_minutes = 10
1003
+ elif confidence >= 0.6:
1004
+ expected_minutes = 18
1005
+ else:
1006
+ expected_minutes = 25
1007
+
1008
+ reasoning = f"Local enhanced analysis: Strong buy signal with {buy_conditions}/{total_conditions} conditions met. Strategy: {strategy}. Confidence: {confidence:.2f}"
1009
+ if rsi_warning:
1010
+ reasoning += " (RSI warning - trading with caution)"
1011
+
1012
+ return {
1013
+ "action": action,
1014
+ "reasoning": reasoning,
1015
+ "trade_type": trade_type,
1016
+ "stop_loss": stop_loss,
1017
+ "take_profit": take_profit,
1018
+ "expected_target_minutes": expected_minutes,
1019
+ "confidence_level": confidence,
1020
+ "model_source": "local",
1021
+ "strategy": strategy
1022
+ }
1023
+
1024
+ def local_re_analyze_trade(trade_data, processed_data):
1025
+ current_price = processed_data['current_price']
1026
+ stop_loss = trade_data['stop_loss']
1027
+ take_profit = trade_data['take_profit']
1028
+ action = "HOLD"
1029
+ reasoning = "Local re-analysis: No significant change to trigger an update or close."
1030
+ if stop_loss and current_price <= stop_loss:
1031
+ action = "CLOSE_TRADE"
1032
+ reasoning = "Local re-analysis: Stop loss has been hit."
1033
+ elif take_profit and current_price >= take_profit:
1034
+ action = "CLOSE_TRADE"
1035
+ reasoning = "Local re-analysis: Take profit has been hit."
1036
+
1037
+ strategy = trade_data.get('strategy', 'GENERIC')
1038
+ if strategy == 'unknown':
1039
+ strategy = trade_data.get('decision_data', {}).get('strategy', 'GENERIC')
1040
+
1041
+ return {
1042
+ "action": action,
1043
+ "reasoning": reasoning,
1044
+ "new_stop_loss": None,
1045
+ "new_take_profit": None,
1046
+ "new_expected_minutes": None,
1047
+ "model_source": "local",
1048
+ "strategy": strategy
1049
+ }
1050
+
1051
+ print("✅ ENHANCED LLM Service loaded successfully - ADVANCED PATTERN ANALYSIS - Performance Tracking - Real-time Pattern Integration")
ML (31).py ADDED
@@ -0,0 +1,1402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ML.py - الإصدار المحدث مع إصلاح الأخطاء الحرجة
2
+ import pandas as pd
3
+ import pandas_ta as ta
4
+ import numpy as np
5
+ from datetime import datetime
6
+ import asyncio
7
+ from data_manager import DataManager
8
+
9
+ class AdvancedTechnicalAnalyzer:
10
+ def __init__(self):
11
+ self.indicators_config = {
12
+ 'trend': ['ema_9', 'ema_21', 'ema_50', 'ema_200', 'ichimoku', 'adx', 'parabolic_sar', 'dmi'],
13
+ 'momentum': ['rsi', 'stoch_rsi', 'macd', 'williams_r', 'cci', 'awesome_oscillator', 'momentum'],
14
+ 'volatility': ['bbands', 'atr', 'keltner', 'donchian', 'rvi'],
15
+ 'volume': ['vwap', 'obv', 'mfi', 'volume_profile', 'ad', 'volume_oscillator'],
16
+ 'cycle': ['hull_ma', 'supertrend', 'zigzag', 'fisher_transform']
17
+ }
18
+
19
+ def calculate_all_indicators(self, dataframe, timeframe):
20
+ if dataframe.empty:
21
+ return {}
22
+ indicators = {}
23
+ indicators.update(self._calculate_trend_indicators(dataframe))
24
+ indicators.update(self._calculate_momentum_indicators(dataframe))
25
+ indicators.update(self._calculate_volatility_indicators(dataframe))
26
+ indicators.update(self._calculate_volume_indicators(dataframe))
27
+ indicators.update(self._calculate_cycle_indicators(dataframe))
28
+ return indicators
29
+
30
+ def _calculate_trend_indicators(self, dataframe):
31
+ trend = {}
32
+ trend['ema_9'] = float(ta.ema(dataframe['close'], length=9).iloc[-1]) if len(dataframe) >= 9 else None
33
+ trend['ema_21'] = float(ta.ema(dataframe['close'], length=21).iloc[-1]) if len(dataframe) >= 21 else None
34
+ trend['ema_50'] = float(ta.ema(dataframe['close'], length=50).iloc[-1]) if len(dataframe) >= 50 else None
35
+ trend['ema_200'] = float(ta.ema(dataframe['close'], length=200).iloc[-1]) if len(dataframe) >= 200 else None
36
+
37
+ if len(dataframe) >= 26:
38
+ ichimoku = ta.ichimoku(dataframe['high'], dataframe['low'], dataframe['close'])
39
+ if ichimoku is not None:
40
+ trend['ichimoku_conversion'] = float(ichimoku[0]['ITS_9'].iloc[-1]) if not ichimoku[0]['ITS_9'].empty else None
41
+ trend['ichimoku_base'] = float(ichimoku[0]['IKS_26'].iloc[-1]) if not ichimoku[0]['IKS_26'].empty else None
42
+ trend['ichimoku_span_a'] = float(ichimoku[0]['ISA_9'].iloc[-1]) if not ichimoku[0]['ISA_9'].empty else None
43
+ trend['ichimoku_span_b'] = float(ichimoku[0]['ISB_26'].iloc[-1]) if not ichimoku[0]['ISB_26'].empty else None
44
+
45
+ if len(dataframe) >= 14:
46
+ adx_result = ta.adx(dataframe['high'], dataframe['low'], dataframe['close'], length=14)
47
+ if adx_result is not None:
48
+ trend['adx'] = float(adx_result['ADX_14'].iloc[-1]) if not adx_result['ADX_14'].empty else None
49
+ trend['dmi_plus'] = float(adx_result['DMP_14'].iloc[-1]) if not adx_result['DMP_14'].empty else None
50
+ trend['dmi_minus'] = float(adx_result['DMN_14'].iloc[-1]) if not adx_result['DMN_14'].empty else None
51
+
52
+ if len(dataframe) >= 5:
53
+ psar = ta.psar(dataframe['high'], dataframe['low'], dataframe['close'])
54
+ if psar is not None:
55
+ trend['psar'] = float(psar['PSARl_0.02_0.2'].iloc[-1]) if not psar['PSARl_0.02_0.2'].empty else None
56
+ return {key: value for key, value in trend.items() if value is not None}
57
+
58
+ def _calculate_momentum_indicators(self, dataframe):
59
+ momentum = {}
60
+ if len(dataframe) >= 14:
61
+ rsi = ta.rsi(dataframe['close'], length=14)
62
+ momentum['rsi'] = float(rsi.iloc[-1]) if not rsi.empty else None
63
+
64
+ if len(dataframe) >= 14:
65
+ stoch_rsi = ta.stochrsi(dataframe['close'], length=14)
66
+ if stoch_rsi is not None:
67
+ momentum['stoch_rsi_k'] = float(stoch_rsi['STOCHRSIk_14_14_3_3'].iloc[-1]) if not stoch_rsi['STOCHRSIk_14_14_3_3'].empty else None
68
+ momentum['stoch_rsi_d'] = float(stoch_rsi['STOCHRSId_14_14_3_3'].iloc[-1]) if not stoch_rsi['STOCHRSId_14_14_3_3'].empty else None
69
+
70
+ if len(dataframe) >= 26:
71
+ macd = ta.macd(dataframe['close'])
72
+ if macd is not None:
73
+ momentum['macd_line'] = float(macd['MACD_12_26_9'].iloc[-1]) if not macd['MACD_12_26_9'].empty else None
74
+ momentum['macd_signal'] = float(macd['MACDs_12_26_9'].iloc[-1]) if not macd['MACDs_12_26_9'].empty else None
75
+ momentum['macd_hist'] = float(macd['MACDh_12_26_9'].iloc[-1]) if not macd['MACDh_12_26_9'].empty else None
76
+
77
+ if len(dataframe) >= 14:
78
+ williams = ta.willr(dataframe['high'], dataframe['low'], dataframe['close'], length=14)
79
+ momentum['williams_r'] = float(williams.iloc[-1]) if not williams.empty else None
80
+
81
+ if len(dataframe) >= 20:
82
+ cci = ta.cci(dataframe['high'], dataframe['low'], dataframe['close'], length=20)
83
+ momentum['cci'] = float(cci.iloc[-1]) if not cci.empty else None
84
+
85
+ if len(dataframe) >= 34:
86
+ awesome_oscillator = ta.ao(dataframe['high'], dataframe['low'])
87
+ momentum['awesome_oscillator'] = float(awesome_oscillator.iloc[-1]) if not awesome_oscillator.empty else None
88
+
89
+ if len(dataframe) >= 10:
90
+ momentum_indicator = ta.mom(dataframe['close'], length=10)
91
+ momentum['momentum'] = float(momentum_indicator.iloc[-1]) if not momentum_indicator.empty else None
92
+ return {key: value for key, value in momentum.items() if value is not None}
93
+
94
+ def _calculate_volatility_indicators(self, dataframe):
95
+ volatility = {}
96
+ if len(dataframe) >= 20:
97
+ bollinger_bands = ta.bbands(dataframe['close'], length=20, std=2)
98
+ if bollinger_bands is not None:
99
+ volatility['bb_upper'] = float(bollinger_bands['BBU_20_2.0'].iloc[-1]) if not bollinger_bands['BBU_20_2.0'].empty else None
100
+ volatility['bb_middle'] = float(bollinger_bands['BBM_20_2.0'].iloc[-1]) if not bollinger_bands['BBM_20_2.0'].empty else None
101
+ volatility['bb_lower'] = float(bollinger_bands['BBL_20_2.0'].iloc[-1]) if not bollinger_bands['BBL_20_2.0'].empty else None
102
+ if all(key in volatility for key in ['bb_upper', 'bb_lower', 'bb_middle']):
103
+ volatility['bb_width'] = (volatility['bb_upper'] - volatility['bb_lower']) / volatility['bb_middle']
104
+
105
+ if len(dataframe) >= 14:
106
+ average_true_range = ta.atr(dataframe['high'], dataframe['low'], dataframe['close'], length=14)
107
+ volatility['atr'] = float(average_true_range.iloc[-1]) if not average_true_range.empty else None
108
+ if volatility['atr']:
109
+ volatility['atr_percent'] = volatility['atr'] / dataframe['close'].iloc[-1]
110
+
111
+ if len(dataframe) >= 20:
112
+ keltner_channel = ta.kc(dataframe['high'], dataframe['low'], dataframe['close'], length=20)
113
+ if keltner_channel is not None:
114
+ volatility['kc_upper'] = float(keltner_channel['KCUe_20_2'].iloc[-1]) if not keltner_channel['KCUe_20_2'].empty else None
115
+ volatility['kc_lower'] = float(keltner_channel['KCLe_20_2'].iloc[-1]) if not keltner_channel['KCLe_20_2'].empty else None
116
+
117
+ if len(dataframe) >= 20:
118
+ donchian_channel = ta.donchian(dataframe['high'], dataframe['low'], length=20)
119
+ if donchian_channel is not None:
120
+ volatility['dc_upper'] = float(donchian_channel['DCU_20_20'].iloc[-1]) if not donchian_channel['DCU_20_20'].empty else None
121
+ volatility['dc_lower'] = float(donchian_channel['DCL_20_20'].iloc[-1]) if not donchian_channel['DCL_20_20'].empty else None
122
+
123
+ if len(dataframe) >= 14:
124
+ relative_volatility_index = ta.rvi(dataframe['close'], length=14)
125
+ volatility['rvi'] = float(relative_volatility_index.iloc[-1]) if not relative_volatility_index.empty else None
126
+ return {key: value for key, value in volatility.items() if value is not None}
127
+
128
+ def _calculate_volume_indicators(self, dataframe):
129
+ volume = {}
130
+ if len(dataframe) >= 1:
131
+ volume_weighted_average_price = ta.vwap(dataframe['high'], dataframe['low'], dataframe['close'], dataframe['volume'])
132
+ volume['vwap'] = float(volume_weighted_average_price.iloc[-1]) if not volume_weighted_average_price.empty else None
133
+
134
+ on_balance_volume = ta.obv(dataframe['close'], dataframe['volume'])
135
+ volume['obv'] = float(on_balance_volume.iloc[-1]) if not on_balance_volume.empty else None
136
+
137
+ if len(dataframe) >= 14:
138
+ money_flow_index = ta.mfi(dataframe['high'], dataframe['low'], dataframe['close'], dataframe['volume'], length=14)
139
+ volume['mfi'] = float(money_flow_index.iloc[-1]) if not money_flow_index.empty else None
140
+
141
+ accumulation_distribution = ta.ad(dataframe['high'], dataframe['low'], dataframe['close'], dataframe['volume'])
142
+ volume['ad_line'] = float(accumulation_distribution.iloc[-1]) if not accumulation_distribution.empty else None
143
+
144
+ if len(dataframe) >= 20:
145
+ volume_oscillator = ta.pvo(dataframe['volume'])
146
+ if volume_oscillator is not None:
147
+ volume['volume_oscillator'] = float(volume_oscillator['PVO_12_26_9'].iloc[-1]) if not volume_oscillator['PVO_12_26_9'].empty else None
148
+
149
+ volume['volume_avg_20'] = float(dataframe['volume'].tail(20).mean()) if len(dataframe) >= 20 else None
150
+ if volume['volume_avg_20'] and volume['volume_avg_20'] > 0:
151
+ volume['volume_ratio'] = float(dataframe['volume'].iloc[-1] / volume['volume_avg_20'])
152
+ return {key: value for key, value in volume.items() if value is not None}
153
+
154
+ def _calculate_cycle_indicators(self, dataframe):
155
+ cycle = {}
156
+ if len(dataframe) >= 9:
157
+ hull_moving_average = ta.hma(dataframe['close'], length=9)
158
+ cycle['hull_ma'] = float(hull_moving_average.iloc[-1]) if not hull_moving_average.empty else None
159
+
160
+ if len(dataframe) >= 10:
161
+ supertrend = ta.supertrend(dataframe['high'], dataframe['low'], dataframe['close'], length=10, multiplier=3)
162
+ if supertrend is not None:
163
+ cycle['supertrend'] = float(supertrend['SUPERT_10_3.0'].iloc[-1]) if not supertrend['SUPERT_10_3.0'].empty else None
164
+ cycle['supertrend_direction'] = float(supertrend['SUPERTd_10_3.0'].iloc[-1]) if not supertrend['SUPERTd_10_3.0'].empty else None
165
+
166
+ if len(dataframe) >= 10:
167
+ fisher_transform = ta.fisher(dataframe['high'], dataframe['low'], length=10)
168
+ if fisher_transform is not None:
169
+ cycle['fisher_transform'] = float(fisher_transform['FISHERT_10_1'].iloc[-1]) if not fisher_transform['FISHERT_10_1'].empty else None
170
+ return {key: value for key, value in cycle.items() if value is not None}
171
+
172
+ class PatternEnhancedStrategyEngine:
173
+ """محرك استراتيجيات محسن مع دعم متقدم للأنماط البيانية"""
174
+
175
+ def __init__(self, data_manager, learning_engine):
176
+ self.data_manager = data_manager
177
+ self.learning_engine = learning_engine
178
+ self.pattern_success_tracker = PatternPerformanceTracker()
179
+
180
+ async def enhance_strategy_with_patterns(self, strategy_scores, pattern_analysis, symbol):
181
+ """تعزيز درجات الاستراتيجية بناءً على تحليل الأنماط"""
182
+ if not pattern_analysis or pattern_analysis.get('pattern_detected') in ['no_clear_pattern', 'insufficient_data']:
183
+ return strategy_scores
184
+
185
+ pattern_confidence = pattern_analysis.get('pattern_confidence', 0)
186
+ pattern_name = pattern_analysis.get('pattern_detected', '')
187
+ predicted_direction = pattern_analysis.get('predicted_direction', '')
188
+
189
+ # فقط تعزيز إذا كانت الثقة في النمط عالية
190
+ if pattern_confidence >= 0.7:
191
+ enhancement_factor = self._calculate_pattern_enhancement(pattern_confidence, pattern_name)
192
+
193
+ # تحديد الاستراتيجيات المناسبة للنمط
194
+ enhanced_strategies = self._get_pattern_appropriate_strategies(pattern_name, predicted_direction)
195
+
196
+ for strategy in enhanced_strategies:
197
+ if strategy in strategy_scores:
198
+ strategy_scores[strategy] *= enhancement_factor
199
+ print(f"🎯 Enhanced {strategy} for {symbol} by {enhancement_factor:.1%} due to {pattern_name} pattern")
200
+
201
+ return strategy_scores
202
+
203
+ def _calculate_pattern_enhancement(self, pattern_confidence, pattern_name):
204
+ """حساب عامل التعزيز بناءً على ثقة النمط ونوعه"""
205
+ base_enhancement = 1.0 + (pattern_confidence * 0.5) # حتى 50% زيادة
206
+
207
+ # أنماط عالية المصداقية تحصل على تعزيز إضافي
208
+ high_reliability_patterns = ['Double Top', 'Double Bottom', 'Head & Shoulders', 'Cup and Handle']
209
+ if pattern_name in high_reliability_patterns:
210
+ base_enhancement *= 1.2
211
+
212
+ return min(base_enhancement, 2.0) # حد أقصى 100% زيادة
213
+
214
+ def _get_pattern_appropriate_strategies(self, pattern_name, direction):
215
+ """الحصول على الاستراتيجيات المناسبة لنوع النمط"""
216
+ reversal_patterns = ['Double Top', 'Double Bottom', 'Head & Shoulders', 'Triple Top', 'Triple Bottom']
217
+ continuation_patterns = ['Flags', 'Pennants', 'Triangles', 'Rectangles']
218
+
219
+ if pattern_name in reversal_patterns:
220
+ if direction == 'down':
221
+ return ['breakout_momentum', 'trend_following']
222
+ else:
223
+ return ['mean_reversion', 'breakout_momentum']
224
+
225
+ elif pattern_name in continuation_patterns:
226
+ return ['trend_following', 'breakout_momentum']
227
+
228
+ else: # أنماط أخرى
229
+ return ['breakout_momentum', 'hybrid_ai']
230
+
231
+ class PatternPerformanceTracker:
232
+ """متتبع أداء الأنماط البيانية"""
233
+
234
+ def __init__(self):
235
+ self.pattern_performance = {}
236
+ self.pattern_success_rates = {}
237
+
238
+ async def track_pattern_outcome(self, symbol, pattern_analysis, success, profit_percent):
239
+ """تتبع نتيجة النمط البياني"""
240
+ if not pattern_analysis:
241
+ return
242
+
243
+ pattern_name = pattern_analysis.get('pattern_detected')
244
+ confidence = pattern_analysis.get('pattern_confidence', 0)
245
+
246
+ if pattern_name not in ['no_clear_pattern', 'insufficient_data']:
247
+ if pattern_name not in self.pattern_performance:
248
+ self.pattern_performance[pattern_name] = {
249
+ 'total_trades': 0,
250
+ 'successful_trades': 0,
251
+ 'total_profit': 0,
252
+ 'total_confidence': 0
253
+ }
254
+
255
+ stats = self.pattern_performance[pattern_name]
256
+ stats['total_trades'] += 1
257
+ stats['total_confidence'] += confidence
258
+
259
+ if success:
260
+ stats['successful_trades'] += 1
261
+ stats['total_profit'] += profit_percent
262
+
263
+ success_rate = stats['successful_trades'] / stats['total_trades']
264
+ avg_profit = stats['total_profit'] / stats['successful_trades'] if stats['successful_trades'] > 0 else 0
265
+ avg_confidence = stats['total_confidence'] / stats['total_trades']
266
+
267
+ print(f"📊 Pattern Performance: {pattern_name} - "
268
+ f"Success: {success_rate:.1%} - Avg Profit: {avg_profit:.2f}% - "
269
+ f"Avg Confidence: {avg_confidence:.1%}")
270
+
271
+ def get_pattern_reliability(self, pattern_name):
272
+ """الحصول على موثوقية النمط"""
273
+ if pattern_name in self.pattern_performance:
274
+ stats = self.pattern_performance[pattern_name]
275
+ if stats['total_trades'] > 0:
276
+ return stats['successful_trades'] / stats['total_trades']
277
+ return 0.5 # قيمة افتراضية
278
+
279
+ class MultiStrategyEngine:
280
+ def __init__(self, data_manager, learning_engine):
281
+ self.data_manager = data_manager
282
+ self.learning_engine = learning_engine
283
+ self.pattern_enhancer = PatternEnhancedStrategyEngine(data_manager, learning_engine)
284
+ self.strategies = {
285
+ 'trend_following': self._trend_following_strategy,
286
+ 'mean_reversion': self._mean_reversion_strategy,
287
+ 'breakout_momentum': self._breakout_momentum_strategy,
288
+ 'volume_spike': self._volume_spike_strategy,
289
+ 'whale_tracking': self._whale_tracking_strategy,
290
+ 'pattern_recognition': self._pattern_recognition_strategy,
291
+ 'hybrid_ai': self._hybrid_ai_strategy
292
+ }
293
+
294
+ async def evaluate_all_strategies(self, symbol_data, market_context):
295
+ """تقييم جميع الاستراتيجيات مع استخدام أوزان نظام التعلم - الإصدار المحسّن"""
296
+ try:
297
+ # ✅ الحصول على الأوزان المحسنة من نظام التعلم
298
+ market_condition = market_context.get('market_trend', 'sideways_market')
299
+
300
+ # ✅ التحقق من وجود learning_engine وتهيئته
301
+ if self.learning_engine and hasattr(self.learning_engine, 'initialized') and self.learning_engine.initialized:
302
+ try:
303
+ optimized_weights = await self.learning_engine.get_optimized_strategy_weights(market_condition)
304
+ print(f"🎯 الأوزان المستخدمة: {optimized_weights}")
305
+ except Exception as e:
306
+ print(f"⚠️ فشل الحصول على الأوزان المحسنة: {e}")
307
+ optimized_weights = await self.get_default_weights()
308
+ else:
309
+ print("⚠️ نظام التعلم غير متوفر، استخدام الأوزان الافتراضية")
310
+ optimized_weights = await self.get_default_weights()
311
+
312
+ strategy_scores = {}
313
+ base_scores = {} # ✅ تخزين الدرجات الأساسية
314
+
315
+ # ✅ تقييم كل استراتيجية مع تطبيق الأوزان
316
+ for strategy_name, strategy_function in self.strategies.items():
317
+ try:
318
+ base_score = await strategy_function(symbol_data, market_context)
319
+ base_scores[strategy_name] = base_score # ✅ حفظ الدرجة الأساسية
320
+
321
+ # ✅ تطبيق الوزن المحسن
322
+ weight = optimized_weights.get(strategy_name, 0.1)
323
+ weighted_score = base_score * weight
324
+
325
+ strategy_scores[strategy_name] = min(weighted_score, 1.0)
326
+
327
+ print(f"📊 {strategy_name}: {base_score:.3f} × {weight:.3f} = {weighted_score:.3f}")
328
+
329
+ except Exception as error:
330
+ print(f"⚠️ Strategy {strategy_name} failed: {error}")
331
+ base_score = await self._fallback_strategy_score(strategy_name, symbol_data, market_context)
332
+ base_scores[strategy_name] = base_score
333
+ strategy_scores[strategy_name] = base_score * optimized_weights.get(strategy_name, 0.1)
334
+
335
+ # ✅ تعزيز الاستراتيجيات بناءً على تحليل الأنماط
336
+ pattern_analysis = symbol_data.get('pattern_analysis')
337
+ if pattern_analysis:
338
+ strategy_scores = await self.pattern_enhancer.enhance_strategy_with_patterns(
339
+ strategy_scores, pattern_analysis, symbol_data.get('symbol')
340
+ )
341
+
342
+ # ✅ تحديد أفضل استراتيجية بناءً على الدرجات الأساسية (بدون أوزان)
343
+ if base_scores:
344
+ best_strategy = max(base_scores.items(), key=lambda x: x[1])
345
+ best_strategy_name = best_strategy[0]
346
+ best_strategy_score = best_strategy[1]
347
+
348
+ symbol_data['recommended_strategy'] = best_strategy_name
349
+ symbol_data['strategy_confidence'] = best_strategy_score
350
+
351
+ print(f"🏆 أفضل استراتيجية (أساسي): {best_strategy_name} بدرجة {best_strategy_score:.3f}")
352
+
353
+ # ✅ تعزيز اختيار الاستراتيجية إذا كان هناك نمط عالي الثقة
354
+ if (pattern_analysis and
355
+ pattern_analysis.get('pattern_confidence', 0) > 0.7 and
356
+ self._is_strategy_pattern_aligned(best_strategy_name, pattern_analysis)):
357
+
358
+ pattern_bonus = pattern_analysis.get('pattern_confidence', 0) * 0.3
359
+ enhanced_confidence = min(best_strategy_score + pattern_bonus, 1.0)
360
+ symbol_data['strategy_confidence'] = enhanced_confidence
361
+ print(f"🎯 تعزيز ثقة الاستراتيجية بواسطة النمط: {enhanced_confidence:.3f}")
362
+
363
+ return strategy_scores, base_scores # ✅ إرجاع كلا النوعين
364
+
365
+ except Exception as error:
366
+ print(f"❌ فشل تقييم الاستراتيجيات: {error}")
367
+ fallback_scores = await self.get_fallback_scores()
368
+ return fallback_scores, fallback_scores
369
+
370
+ def _is_strategy_pattern_aligned(self, strategy_name, pattern_analysis):
371
+ """التحقق من محاذاة الاستراتيجية مع النمط البياني"""
372
+ pattern_direction = pattern_analysis.get('predicted_direction', '')
373
+ pattern_type = pattern_analysis.get('pattern_detected', '')
374
+
375
+ # استراتيجيات التوجه الصعودي
376
+ bullish_strategies = ['trend_following', 'breakout_momentum']
377
+ # استراتيجيات التوجه الهبوطي
378
+ bearish_strategies = ['mean_reversion', 'breakout_momentum']
379
+
380
+ if pattern_direction == 'up' and strategy_name in bullish_strategies:
381
+ return True
382
+ elif pattern_direction == 'down' and strategy_name in bearish_strategies:
383
+ return True
384
+
385
+ return False
386
+
387
+ async def get_default_weights(self):
388
+ """الأوزان الافتراضية عندما لا يتوفر نظام التعلم"""
389
+ return {
390
+ 'trend_following': 0.15, 'mean_reversion': 0.12,
391
+ 'breakout_momentum': 0.18, 'volume_spike': 0.10,
392
+ 'whale_tracking': 0.20, 'pattern_recognition': 0.15,
393
+ 'hybrid_ai': 0.10
394
+ }
395
+
396
+ async def get_fallback_scores(self):
397
+ """درجات احتياطية عند فشل التقييم"""
398
+ return {
399
+ 'trend_following': 0.5, 'mean_reversion': 0.5,
400
+ 'breakout_momentum': 0.5, 'volume_spike': 0.5,
401
+ 'whale_tracking': 0.5, 'pattern_recognition': 0.5,
402
+ 'hybrid_ai': 0.5
403
+ }
404
+
405
+ async def _trend_following_strategy(self, symbol_data, market_context):
406
+ """استراتيجية تتبع الاتجاه المحسنة - درجات أعلى"""
407
+ try:
408
+ score = 0.0
409
+ indicators = symbol_data.get('advanced_indicators', {})
410
+ timeframes = ['4h', '1h', '15m']
411
+
412
+ for timeframe in timeframes:
413
+ if timeframe in indicators:
414
+ timeframe_indicators = indicators[timeframe]
415
+
416
+ # تحقق من محاذاة المتوسطات المتحركة
417
+ if self._check_ema_alignment(timeframe_indicators):
418
+ score += 0.20 # ⬆️ زيادة من 0.15 إلى 0.20
419
+
420
+ # مؤشر ADX للقوة الاتجاهية
421
+ adx_value = timeframe_indicators.get('adx', 0)
422
+ if adx_value > 20: # ⬇️ تخفيض من 25 إلى 20
423
+ score += 0.15 # ⬆️ زيادة من 0.1 إلى 0.15
424
+
425
+ # تحليل الحجم
426
+ volume_ratio = timeframe_indicators.get('volume_ratio', 0)
427
+ if volume_ratio > 1.2: # ⬇️ تخفيض من 1.5 إلى 1.2
428
+ score += 0.10 # ⬆️ زيادة من 0.05 إلى 0.10
429
+
430
+ # ✅ تعزيز بناءً على تحليل الأنماط
431
+ pattern_analysis = symbol_data.get('pattern_analysis')
432
+ if (pattern_analysis and
433
+ pattern_analysis.get('pattern_confidence', 0) > 0.7 and
434
+ pattern_analysis.get('predicted_direction') == 'up'):
435
+ pattern_bonus = pattern_analysis.get('pattern_confidence', 0) * 0.2
436
+ score += pattern_bonus
437
+ print(f"📈 Trend following enhanced by pattern: +{pattern_bonus:.3f}")
438
+
439
+ return min(score, 1.0)
440
+
441
+ except Exception as error:
442
+ print(f"⚠️ Trend following strategy error: {error}")
443
+ return 0.3 # ⬆️ زيادة من 0.3 إلى 0.3 (نفس القيمة)
444
+
445
+ def _check_ema_alignment(self, indicators):
446
+ """التحقق من محاذاة المتوسطات المتحركة"""
447
+ required_emas = ['ema_9', 'ema_21', 'ema_50']
448
+ if all(ema in indicators for ema in required_emas):
449
+ return (indicators['ema_9'] > indicators['ema_21'] > indicators['ema_50'])
450
+ return False
451
+
452
+ async def _mean_reversion_strategy(self, symbol_data, market_context):
453
+ """استراتيجية العودة إلى المتوسط المحسنة - درجات أعلى"""
454
+ try:
455
+ score = 0.0
456
+ current_price = symbol_data['current_price']
457
+ indicators = symbol_data.get('advanced_indicators', {})
458
+
459
+ if '1h' in indicators:
460
+ hourly_indicators = indicators['1h']
461
+
462
+ # تحليل Bollinger Bands
463
+ if all(key in hourly_indicators for key in ['bb_upper', 'bb_lower', 'bb_middle']):
464
+ position_in_band = (current_price - hourly_indicators['bb_lower']) / (hourly_indicators['bb_upper'] - hourly_indicators['bb_lower'])
465
+
466
+ if position_in_band < 0.1 and hourly_indicators.get('rsi', 50) < 35:
467
+ score += 0.45 # ⬆️ زيادة من 0.4 إلى 0.45
468
+ if position_in_band > 0.9 and hourly_indicators.get('rsi', 50) > 65:
469
+ score += 0.45 # ⬆️ زيادة من 0.4 إلى 0.45
470
+
471
+ # تحليل RSI
472
+ rsi_value = hourly_indicators.get('rsi', 50)
473
+ if rsi_value < 30:
474
+ score += 0.35 # ⬆️ زيادة من 0.3 إلى 0.35
475
+ elif rsi_value > 70:
476
+ score += 0.35 # ⬆️ زيادة من 0.3 إلى 0.35
477
+
478
+ # ✅ تعزيز بناءً على تحليل الأنماط
479
+ pattern_analysis = symbol_data.get('pattern_analysis')
480
+ if (pattern_analysis and
481
+ pattern_analysis.get('pattern_confidence', 0) > 0.7 and
482
+ pattern_analysis.get('predicted_direction') in ['up', 'down']):
483
+ pattern_bonus = pattern_analysis.get('pattern_confidence', 0) * 0.15
484
+ score += pattern_bonus
485
+ print(f"🔄 Mean reversion enhanced by pattern: +{pattern_bonus:.3f}")
486
+
487
+ return min(score, 1.0)
488
+
489
+ except Exception as error:
490
+ print(f"⚠️ Mean reversion strategy error: {error}")
491
+ return 0.3
492
+
493
+ async def _breakout_momentum_strategy(self, symbol_data, market_context):
494
+ """استراتيجية كسر الزخم المحسنة - درجات أعلى"""
495
+ try:
496
+ score = 0.0
497
+ indicators = symbol_data.get('advanced_indicators', {})
498
+
499
+ for timeframe in ['1h', '15m', '5m']: # ✅ إضافة timeframe إضافية
500
+ if timeframe in indicators:
501
+ timeframe_indicators = indicators[timeframe]
502
+
503
+ # ✅ تحليل الحجم - عتبات مخفضة
504
+ volume_ratio = timeframe_indicators.get('volume_ratio', 0)
505
+ if volume_ratio > 1.8: # ⬇️ تخفيض من 2.0 إلى 1.8
506
+ score += 0.25 # ⬆️ زيادة من 0.2 إلى 0.25
507
+ elif volume_ratio > 1.3: # ✅ إضافة شرط وسيط
508
+ score += 0.15
509
+
510
+ # ✅ تحليل MACD
511
+ if timeframe_indicators.get('macd_hist', 0) > 0:
512
+ score += 0.20 # ⬆️ زيادة من 0.15 إلى 0.20
513
+
514
+ # ✅ تحليل VWAP
515
+ if 'vwap' in timeframe_indicators and symbol_data['current_price'] > timeframe_indicators['vwap']:
516
+ score += 0.15 # ⬆️ زيادة من 0.1 إلى 0.15
517
+
518
+ # ✅ إضافة شرط RSI إضافي
519
+ rsi_value = timeframe_indicators.get('rsi', 50)
520
+ if 40 <= rsi_value <= 70: # نطاق RSI صحي
521
+ score += 0.10
522
+
523
+ # ✅ تعزيز بناءً على تحليل الأنماط - تأثير كبير على breakout
524
+ pattern_analysis = symbol_data.get('pattern_analysis')
525
+ if pattern_analysis and pattern_analysis.get('pattern_confidence', 0) > 0.6:
526
+ pattern_bonus = pattern_analysis.get('pattern_confidence', 0) * 0.3
527
+ score += pattern_bonus
528
+ print(f"🚀 Breakout momentum significantly enhanced by pattern: +{pattern_bonus:.3f}")
529
+
530
+ # ✅ ضمان حد أدنى للدرجة إذا كانت هناك إشارات إيجابية
531
+ if score > 0.2:
532
+ score = max(score, 0.4) # ⬆️ ضمان حد أدنى 0.4 إذا كانت هناك إشارات
533
+
534
+ return min(score, 1.0)
535
+
536
+ except Exception as error:
537
+ print(f"⚠️ Breakout momentum strategy error: {error}")
538
+ return 0.4 # ⬆️ زيادة من 0.3 إلى 0.4
539
+
540
+ async def _volume_spike_strategy(self, symbol_data, market_context):
541
+ """استراتيجية ارتفاع الحجم المحسنة - درجات أعلى"""
542
+ try:
543
+ score = 0.0
544
+ indicators = symbol_data.get('advanced_indicators', {})
545
+
546
+ for timeframe in ['1h', '15m', '5m']:
547
+ if timeframe in indicators:
548
+ volume_ratio = indicators[timeframe].get('volume_ratio', 0)
549
+ if volume_ratio > 3.0:
550
+ score += 0.45 # ⬆️ زيادة من 0.4 إلى 0.45
551
+ elif volume_ratio > 2.0:
552
+ score += 0.25 # ⬆️ زيادة من 0.2 إلى 0.25
553
+ elif volume_ratio > 1.5: # ✅ إضافة شرط وسيط
554
+ score += 0.15
555
+
556
+ # ✅ تعزيز بناءً على تحليل الأنماط مع ارتفاع الحجم
557
+ pattern_analysis = symbol_data.get('pattern_analysis')
558
+ if (pattern_analysis and
559
+ pattern_analysis.get('pattern_confidence', 0) > 0.7 and
560
+ any(indicators[tf].get('volume_ratio', 0) > 2.0 for tf in ['1h', '15m'] if tf in indicators)):
561
+ pattern_bonus = pattern_analysis.get('pattern_confidence', 0) * 0.2
562
+ score += pattern_bonus
563
+ print(f"💧 Volume spike enhanced by pattern: +{pattern_bonus:.3f}")
564
+
565
+ return min(score, 1.0)
566
+
567
+ except Exception as error:
568
+ print(f"⚠️ Volume spike strategy error: {error}")
569
+ return 0.3
570
+
571
+ async def _whale_tracking_strategy(self, symbol_data, market_context):
572
+ """استراتيجية تتبع الحيتان المحسنة - درجات أعلى"""
573
+ try:
574
+ score = 0.0
575
+ # ✅ الإصلاح: استخدام الدالة الآمنة الجديدة
576
+ whale_data = await self.data_manager.get_whale_data_safe_async(symbol_data['symbol'])
577
+
578
+ # استخدام البيانات الحقيقية فقط
579
+ if not whale_data.get('data_available', False):
580
+ return 0.2 # ⬆️ زيادة من 0.1 إلى 0.2 (قيمة أساسية أعلى)
581
+
582
+ total_transactions = whale_data.get('transfer_count', 0)
583
+ whale_volume = whale_data.get('total_volume', 0)
584
+
585
+ # ✅ معايير مرنة للسماح بمزيد من المرشحين
586
+ if total_transactions >= 2: # ⬇️ تخفيض من 3 إلى 2
587
+ score += 0.35 # ⬆️ زيادة من 0.3 إلى 0.35
588
+ elif total_transactions >= 1: # ⬇️ تخفيض من 5 إلى 1
589
+ score += 0.25 # ⬆️ زيادة من 0.15 إلى 0.25
590
+
591
+ if whale_volume > 25000: # ⬇️ تخفيض من 50000 إلى 25000
592
+ score += 0.25 # ⬆️ زيادة من 0.2 إلى 0.25
593
+ elif whale_volume > 5000: # ⬇️ تخفيض من 10000 إلى 5000
594
+ score += 0.15 # ⬆️ زيادة من 0.1 إلى 0.15
595
+
596
+ # ✅ إضافة نقاط إضافية بناءً على نشاط الحيتان العام
597
+ general_whale = market_context.get('general_whale_activity', {})
598
+ if general_whale.get('data_available', False) and general_whale.get('transaction_count', 0) > 0:
599
+ score += 0.15 # ⬆️ زيادة من 0.1 إلى 0.15
600
+
601
+ return min(score, 1.0)
602
+
603
+ except Exception as error:
604
+ print(f"⚠️ Whale tracking failed: {error}")
605
+ return 0.2 # ⬆️ زيادة من 0.1 إلى 0.2
606
+
607
+ async def _pattern_recognition_strategy(self, symbol_data, market_context):
608
+ """استراتيجية التعرف على الأنماط المحسنة - درجات أعلى"""
609
+ try:
610
+ score = 0.0
611
+ indicators = symbol_data.get('advanced_indicators', {})
612
+
613
+ # ✅ تعزيز كبير بناءً على تحليل الأنماط من LLM
614
+ pattern_analysis = symbol_data.get('pattern_analysis')
615
+ if pattern_analysis and pattern_analysis.get('pattern_confidence', 0) > 0.6:
616
+ score += pattern_analysis.get('pattern_confidence', 0) * 0.8
617
+ print(f"🎯 Pattern recognition significantly enhanced: +{score:.3f}")
618
+ else:
619
+ # التحليل التقليدي إذا لم يكن هناك نمط من LLM
620
+ for timeframe in ['4h', '1h']:
621
+ if timeframe in indicators:
622
+ timeframe_indicators = indicators[timeframe]
623
+
624
+ # نمط الزخم الصعودي
625
+ if (timeframe_indicators.get('rsi', 50) > 60 and
626
+ timeframe_indicators.get('macd_hist', 0) > 0 and
627
+ timeframe_indicators.get('volume_ratio', 0) > 1.5):
628
+ score += 0.35 # ⬆️ زيادة من 0.3 إلى 0.35
629
+
630
+ # نمط الزخم الهبوطي
631
+ if (timeframe_indicators.get('rsi', 50) < 40 and
632
+ timeframe_indicators.get('stoch_rsi_k', 50) < 20):
633
+ score += 0.35 # ⬆️ زيادة من 0.3 إلى 0.35
634
+
635
+ return min(score, 1.0)
636
+
637
+ except Exception as error:
638
+ print(f"⚠️ Pattern recognition strategy error: {error}")
639
+ return 0.3
640
+
641
+ async def _hybrid_ai_strategy(self, symbol_data, market_context):
642
+ """استراتيجية الهجين الذكية المحسنة - درجات أعلى"""
643
+ try:
644
+ score = 0.0
645
+ monte_carlo_probability = symbol_data.get('monte_carlo_probability', 0.5)
646
+ final_score = symbol_data.get('final_score', 0.5)
647
+
648
+ score += monte_carlo_probability * 0.4
649
+ score += final_score * 0.3
650
+
651
+ # تحليل سياق السوق
652
+ if market_context.get('btc_sentiment') == 'BULLISH':
653
+ score += 0.25 # ⬆️ زيادة من 0.2 إلى 0.25
654
+ elif market_context.get('btc_sentiment') == 'BEARISH':
655
+ score -= 0.08 # ⬆️ تخفيض من 0.1 إلى 0.08
656
+
657
+ # تحليل نشاط الحيتان العام
658
+ whale_activity = market_context.get('general_whale_activity', {})
659
+ if whale_activity.get('sentiment') == 'BULLISH':
660
+ score += 0.15 # ⬆️ زيادة من 0.1 إلى 0.15
661
+
662
+ # ✅ تعزيز بناءً على تحليل الأنماط
663
+ pattern_analysis = symbol_data.get('pattern_analysis')
664
+ if pattern_analysis and pattern_analysis.get('pattern_confidence', 0) > 0.7:
665
+ pattern_bonus = pattern_analysis.get('pattern_confidence', 0) * 0.25
666
+ score += pattern_bonus
667
+ print(f"🤖 Hybrid AI enhanced by pattern: +{pattern_bonus:.3f}")
668
+
669
+ return max(0.0, min(score, 1.0))
670
+
671
+ except Exception as error:
672
+ print(f"⚠️ Hybrid AI strategy error: {error}")
673
+ return 0.3
674
+
675
+ async def _fallback_strategy_score(self, strategy_name, symbol_data, market_context):
676
+ """درجات استراتيجية احتياطية محسنة"""
677
+ try:
678
+ base_score = symbol_data.get('final_score', 0.5)
679
+
680
+ if strategy_name == 'trend_following':
681
+ indicators = symbol_data.get('advanced_indicators', {})
682
+ if '1h' in indicators:
683
+ rsi_value = indicators['1h'].get('rsi', 50)
684
+ ema_9 = indicators['1h'].get('ema_9')
685
+ ema_21 = indicators['1h'].get('ema_21')
686
+
687
+ if ema_9 and ema_21 and ema_9 > ema_21 and 40 <= rsi_value <= 60:
688
+ return 0.6
689
+ return 0.4
690
+
691
+ elif strategy_name == 'mean_reversion':
692
+ current_price = symbol_data.get('current_price', 0)
693
+ indicators = symbol_data.get('advanced_indicators', {})
694
+ if '1h' in indicators:
695
+ rsi_value = indicators['1h'].get('rsi', 50)
696
+ bb_lower = indicators['1h'].get('bb_lower')
697
+
698
+ if bb_lower and current_price <= bb_lower * 1.02 and rsi_value < 35:
699
+ return 0.7
700
+ return 0.3
701
+
702
+ elif strategy_name == 'breakout_momentum':
703
+ volume_ratio = symbol_data.get('advanced_indicators', {}).get('1h', {}).get('volume_ratio', 0)
704
+ if volume_ratio > 1.5:
705
+ return 0.6
706
+ return 0.4
707
+
708
+ elif strategy_name == 'whale_tracking':
709
+ whale_data = symbol_data.get('whale_data', {})
710
+ if not whale_data.get('data_available', False):
711
+ return 0.2 # ⬆️ زيادة من 0.1 إلى 0.2
712
+
713
+ total_transactions = whale_data.get('transfer_count', 0)
714
+ if total_transactions >= 3:
715
+ return 0.5
716
+ return 0.3
717
+
718
+ return base_score
719
+
720
+ except Exception as error:
721
+ print(f"⚠️ Fallback strategy failed for {strategy_name}: {error}")
722
+ return 0.3
723
+
724
+ class MLProcessor:
725
+ def __init__(self, market_context, data_manager, learning_engine):
726
+ self.market_context = market_context
727
+ self.data_manager = data_manager
728
+ self.learning_engine = learning_engine
729
+ self.technical_analyzer = AdvancedTechnicalAnalyzer()
730
+ self.strategy_engine = MultiStrategyEngine(data_manager, learning_engine)
731
+ self.pattern_tracker = PatternPerformanceTracker()
732
+
733
+ def _validate_rsi_safety(self, indicators):
734
+ """التحقق العاجل من سلامة مؤشر RSI"""
735
+ rsi_warnings = []
736
+ critical_issues = 0
737
+
738
+ timeframes_to_check = ['5m', '15m', '1h', '4h']
739
+ for timeframe in timeframes_to_check:
740
+ if timeframe in indicators:
741
+ rsi_value = indicators[timeframe].get('rsi')
742
+ if rsi_value:
743
+ if rsi_value > 80:
744
+ rsi_warnings.append(f"🚨 RSI CRITICAL in {timeframe}: {rsi_value} - EXTREME OVERBOUGHT")
745
+ critical_issues += 1
746
+ elif rsi_value > 75:
747
+ rsi_warnings.append(f"⚠️ RSI WARNING in {timeframe}: {rsi_value} - STRONG OVERBOUGHT")
748
+ elif rsi_value > 70:
749
+ rsi_warnings.append(f"📈 RSI HIGH in {timeframe}: {rsi_value} - OVERBOUGHT")
750
+
751
+ # إذا كان هناك إطارين زمنيين أو أكثر في منطقة الخطر، نرفض المرشح
752
+ is_safe = critical_issues < 2
753
+ return is_safe, rsi_warnings
754
+
755
+ def _validate_indicators_quality_enhanced(self, indicators, current_price):
756
+ """تحسين التحقق من جودة المؤشرات"""
757
+ quality_issues = []
758
+
759
+ # التحقق من RSI
760
+ rsi_safe, rsi_warnings = self._validate_rsi_safety(indicators)
761
+ if not rsi_safe:
762
+ quality_issues.extend(rsi_warnings)
763
+
764
+ # التحقق من تناقض المؤشرات
765
+ bullish_signals = 0
766
+ bearish_signals = 0
767
+
768
+ for timeframe, data in indicators.items():
769
+ # إشارات صعودية
770
+ if data.get('macd_hist', 0) > 0:
771
+ bullish_signals += 1
772
+ if data.get('rsi', 50) > 70: # RSI مرتفع يعتبر إشارة بيع
773
+ bearish_signals += 1
774
+ if 'ema_9' in data and 'ema_21' in data:
775
+ if data['ema_9'] > data['ema_21']:
776
+ bullish_signals += 1
777
+
778
+ if bullish_signals > 0 and bearish_signals > bullish_signals:
779
+ quality_issues.append("⚠️ Conflicting signals: More bearish than bullish indicators")
780
+
781
+ return quality_issues
782
+
783
+ def _calculate_enhanced_score_with_safety(self, base_analysis, strategy_scores, quality_issues):
784
+ """حساب النقاط مع مراعاة عوامل السلامة"""
785
+ base_score = base_analysis.get('final_score', 0.5)
786
+ strategy_average = sum(strategy_scores.values()) / len(strategy_scores) if strategy_scores else 0.5
787
+
788
+ # خصم النقاط بناءً على مشاكل الجودة
789
+ safety_penalty = 0.0
790
+ for issue in quality_issues:
791
+ if '🚨 RSI CRITICAL' in issue:
792
+ safety_penalty += 0.3
793
+ elif '⚠️ RSI WARNING' in issue:
794
+ safety_penalty += 0.15
795
+ elif '📈 RSI HIGH' in issue:
796
+ safety_penalty += 0.05
797
+
798
+ enhanced_score = (base_score * 0.4) + (strategy_average * 0.6)
799
+ enhanced_score = max(0.0, enhanced_score - safety_penalty)
800
+
801
+ return min(enhanced_score, 1.0)
802
+
803
+ async def process_and_score_symbol_enhanced(self, raw_data):
804
+ """معالجة وتحليل الرمز مع نظام التعلم والاستراتيجيات - الإصدار المحسّن"""
805
+ try:
806
+ if not raw_data or not raw_data.get('ohlcv'):
807
+ print(f"⚠️ Skipping {raw_data.get('symbol', 'unknown')} - no OHLCV data")
808
+ return None
809
+
810
+ # ✅ تمرير بيانات الشموع الخام للتحليل اللاحق
811
+ raw_data['raw_ohlcv'] = raw_data.get('ohlcv', {})
812
+
813
+ base_analysis = await self.process_and_score_symbol(raw_data)
814
+ if not base_analysis:
815
+ return None
816
+
817
+ try:
818
+ # التحقق المحسن من الجودة
819
+ current_price = base_analysis.get('current_price', 0)
820
+ quality_issues = self._validate_indicators_quality_enhanced(
821
+ base_analysis.get('advanced_indicators', {}),
822
+ current_price
823
+ )
824
+
825
+ # طباعة تحذيرات الجودة
826
+ if quality_issues:
827
+ print(f"🔍 Quality issues for {base_analysis.get('symbol')}:")
828
+ for issue in quality_issues:
829
+ print(f" {issue}")
830
+
831
+ # ✅ تقييم الاستراتيجيات باستخدام نظام التعلم مع التحقق من الوجود
832
+ if hasattr(self, 'strategy_engine') and self.strategy_engine:
833
+ strategy_scores, base_scores = await self.strategy_engine.evaluate_all_strategies(base_analysis, self.market_context)
834
+ base_analysis['strategy_scores'] = strategy_scores
835
+ base_analysis['base_strategy_scores'] = base_scores # ✅ حفظ الدرجات الأساسية
836
+
837
+ # ✅ تحديد أفضل استراتيجية بناءً على الدرجات الأساسية (بدون أوزان)
838
+ if base_scores:
839
+ best_strategy = max(base_scores.items(), key=lambda x: x[1])
840
+ best_strategy_name = best_strategy[0]
841
+ best_strategy_score = best_strategy[1]
842
+
843
+ base_analysis['recommended_strategy'] = best_strategy_name
844
+ base_analysis['strategy_confidence'] = best_strategy_score
845
+
846
+ print(f"🎯 أفضل استراتيجية لـ {base_analysis.get('symbol')}: {best_strategy_name} (ثقة: {best_strategy_score:.3f})")
847
+
848
+ # ✅ تخفيض عتبة الثقة للسماح بمزيد من الاستراتيجيات
849
+ if best_strategy_score > 0.3: # ⬇️ تخفيض من 0.6 إلى 0.3
850
+ base_analysis['target_strategy'] = best_strategy_name
851
+ print(f"✅ استخدام استراتيجية متخصصة: {best_strategy_name}")
852
+ else:
853
+ base_analysis['target_strategy'] = 'GENERIC'
854
+ print(f"🔄 استخدام استراتيجية عامة (ثقة منخفضة: {best_strategy_score:.3f})")
855
+ else:
856
+ base_analysis['recommended_strategy'] = 'GENERIC'
857
+ base_analysis['strategy_confidence'] = 0.3 # ⬆️ زيادة من 0.5 إلى 0.3
858
+ base_analysis['target_strategy'] = 'GENERIC'
859
+ print("🔄 استخدام استراتيجية عامة (لا توجد درجات استراتيجية)")
860
+
861
+ # ✅ استخدام الدالة المحسنة لحساب النقاط
862
+ enhanced_score = self._calculate_enhanced_score_with_safety(
863
+ base_analysis, strategy_scores, quality_issues
864
+ )
865
+
866
+ base_analysis['enhanced_final_score'] = enhanced_score
867
+ else:
868
+ print("⚠️ Strategy engine not available, using base analysis only")
869
+ base_analysis['strategy_scores'] = {}
870
+ base_analysis['enhanced_final_score'] = base_analysis.get('final_score', 0.5)
871
+ base_analysis['recommended_strategy'] = 'GENERIC'
872
+ base_analysis['strategy_confidence'] = 0.3
873
+ base_analysis['target_strategy'] = 'GENERIC'
874
+
875
+ base_analysis['quality_warnings'] = quality_issues
876
+
877
+ except Exception as strategy_error:
878
+ print(f"⚠️ Strategy evaluation failed for {base_analysis.get('symbol')}: {strategy_error}")
879
+ base_analysis['strategy_scores'] = {}
880
+ base_analysis['enhanced_final_score'] = base_analysis.get('final_score', 0.5)
881
+ base_analysis['recommended_strategy'] = 'GENERIC'
882
+ base_analysis['strategy_confidence'] = 0.3 # ⬆️ زيادة من 0.5 إلى 0.3
883
+ base_analysis['target_strategy'] = 'GENERIC'
884
+ base_analysis['quality_warnings'] = ['Strategy evaluation failed']
885
+
886
+ return base_analysis
887
+
888
+ except Exception as error:
889
+ print(f"❌ Enhanced processing failed for {raw_data.get('symbol')}: {error}")
890
+ return await self.process_and_score_symbol(raw_data)
891
+
892
+ def _improve_fibonacci_levels(self, daily_dataframe, current_price):
893
+ """تحسين حساب مستويات Fibonacci لتتوافق مع السعر الحالي"""
894
+ if len(daily_dataframe) < 50:
895
+ return {}
896
+
897
+ # استخدام آخر 50 يومًا لحساب القمة والقاع
898
+ recent_high = float(daily_dataframe['high'].iloc[-50:].max())
899
+ recent_low = float(daily_dataframe['low'].iloc[-50:].min())
900
+
901
+ # إذا كان السعر الحالي خارج النطاق، نعدل النطاق
902
+ if current_price > recent_high:
903
+ recent_high = current_price * 1.05 # نضيف هامش 5%
904
+ if current_price < recent_low:
905
+ recent_low = current_price * 0.95 # نخصم هامش 5%
906
+
907
+ difference = recent_high - recent_low
908
+ if difference <= 0: # تجنب القسمة على الصفر
909
+ return {}
910
+
911
+ return {
912
+ "0.0%": recent_high,
913
+ "23.6%": recent_high - 0.236 * difference,
914
+ "38.2%": recent_high - 0.382 * difference,
915
+ "50.0%": recent_high - 0.50 * difference,
916
+ "61.8%": recent_high - 0.618 * difference,
917
+ "78.6%": recent_high - 0.786 * difference,
918
+ "100.0%": recent_low
919
+ }
920
+
921
+ async def process_and_score_symbol(self, raw_data):
922
+ """معالجة وتحليل الرمز الأساسي"""
923
+ symbol = raw_data['symbol']
924
+ ohlcv_data = raw_data['ohlcv']
925
+ reasons_for_candidacy = raw_data.get('reasons', [])
926
+
927
+ if not ohlcv_data:
928
+ print(f"❌ No OHLCV data for {symbol}")
929
+ return None
930
+
931
+ try:
932
+ all_indicators = {}
933
+ for timeframe, candles in ohlcv_data.items():
934
+ if candles:
935
+ dataframe = pd.DataFrame(candles, columns=['time', 'open', 'high', 'low', 'close', 'volume'])
936
+ dataframe[['open', 'high', 'low', 'close', 'volume']] = dataframe[['open', 'high', 'low', 'close', 'volume']].astype(float)
937
+ all_indicators[timeframe] = self._calculate_indicators(dataframe, timeframe)
938
+
939
+ hourly_dataframe = pd.DataFrame(ohlcv_data.get('1h', []), columns=['time', 'open', 'high', 'low', 'close', 'volume'])
940
+ if hourly_dataframe.empty:
941
+ print(f"❌ Skipping {symbol} due to insufficient 1h data.")
942
+ return None
943
+
944
+ hourly_dataframe[['open', 'high', 'low', 'close', 'volume']] = hourly_dataframe[['open', 'high', 'low', 'close', 'volume']].astype(float)
945
+
946
+ try:
947
+ current_price = float(hourly_dataframe['close'].iloc[-1])
948
+ if ohlcv_data.get('5m'):
949
+ five_minute_dataframe = pd.DataFrame(ohlcv_data['5m'], columns=['time', 'open', 'high', 'low', 'close', 'volume'])
950
+ if not five_minute_dataframe.empty:
951
+ five_minute_dataframe[['open', 'high', 'low', 'close', 'volume']] = five_minute_dataframe[['open', 'high', 'low', 'close', 'volume']].astype(float)
952
+ current_price = float(five_minute_dataframe['close'].iloc[-1])
953
+
954
+ liquidity_score = self._calculate_liquidity_score(hourly_dataframe)
955
+ daily_dataframe = pd.DataFrame(ohlcv_data.get('1d', []), columns=['time', 'open', 'high', 'low', 'close', 'volume'])
956
+ if not daily_dataframe.empty:
957
+ daily_dataframe[['open', 'high', 'low', 'close', 'volume']] = daily_dataframe[['open', 'high', 'low', 'close', 'volume']].astype(float)
958
+
959
+ average_daily_volume = float(daily_dataframe['volume'].mean()) if not daily_dataframe.empty else 0.0
960
+
961
+ # استخدام الدالة المحسنة لحساب مستويات Fibonacci
962
+ fibonacci_levels = self._improve_fibonacci_levels(daily_dataframe, current_price)
963
+
964
+ try:
965
+ whale_data = await self.data_manager.get_whale_data_safe_async(symbol)
966
+ except Exception as whale_error:
967
+ print(f"⚠️ Whale data failed for {symbol}: {whale_error}. No whale data available.")
968
+ whale_data = {
969
+ "transfer_count": 0,
970
+ "total_volume": 0,
971
+ "source": "no_data",
972
+ "data_available": False
973
+ }
974
+
975
+ whale_score = self._calculate_whale_activity_score(whale_data)
976
+ opportunity_classification = self.classify_opportunity_type(all_indicators, current_price)
977
+ initial_score = self._calculate_initial_score(all_indicators, current_price, self.market_context)
978
+ monte_carlo_probability = self._run_monte_carlo_simulation(hourly_dataframe)
979
+
980
+ print(f"🎲 Monte Carlo Simulation for {symbol}: Success Probability = {monte_carlo_probability:.2%}")
981
+
982
+ final_score = (0.35 * initial_score) + (0.50 * monte_carlo_probability) + (0.15 * whale_score)
983
+ final_score *= opportunity_classification['confidence']
984
+
985
+ normalized_indicators = {timeframe: self._normalize_features_corrected(indicators) for timeframe, indicators in all_indicators.items()}
986
+
987
+ return {
988
+ 'symbol': symbol, 'reasons_for_candidacy': reasons_for_candidacy, 'current_price': float(current_price),
989
+ 'liquidity_score': float(liquidity_score) if not np.isnan(liquidity_score) else 0.0, 'avg_daily_volume': float(average_daily_volume),
990
+ 'whale_data': whale_data, 'whale_score': float(whale_score), 'opportunity_type': opportunity_classification,
991
+ 'sentiment_data': self.market_context, 'fibonacci_levels': fibonacci_levels, 'final_score': float(final_score),
992
+ 'initial_score': float(initial_score), 'monte_carlo_probability': float(monte_carlo_probability),
993
+ 'indicators': normalized_indicators, 'advanced_indicators': all_indicators, 'strategy_scores': {},
994
+ 'recommended_strategy': 'GENERIC', 'enhanced_final_score': float(final_score), 'target_strategy': 'GENERIC',
995
+ 'raw_ohlcv': ohlcv_data # ✅ إضافة بيانات الشموع الخام
996
+ }
997
+
998
+ except (KeyError, IndexError) as error:
999
+ print(f"⚠️ Missing data for {symbol}: {error}")
1000
+ return None
1001
+
1002
+ except Exception as error:
1003
+ print(f"❌ Failed to process {symbol}: {error}")
1004
+ import traceback
1005
+ traceback.print_exc()
1006
+ return None
1007
+
1008
+ def _calculate_indicators(self, dataframe, timeframe):
1009
+ """حساب المؤشرات الفنية"""
1010
+ indicators = {}
1011
+ if dataframe.empty:
1012
+ return indicators
1013
+
1014
+ if not isinstance(dataframe.index, pd.DatetimeIndex):
1015
+ try:
1016
+ dataframe['time'] = pd.to_datetime(dataframe['time'], unit='ms')
1017
+ dataframe = dataframe.set_index('time', drop=True)
1018
+ except:
1019
+ dataframe['time'] = pd.to_datetime(dataframe['time'])
1020
+ dataframe = dataframe.set_index('time', drop=True)
1021
+
1022
+ dataframe = dataframe.sort_index()
1023
+
1024
+ if len(dataframe) >= 1 and all(column in dataframe.columns for column in ['high', 'low', 'close', 'volume']):
1025
+ try:
1026
+ typical_price = (dataframe['high'] + dataframe['low'] + dataframe['close']) / 3
1027
+ volume_weighted_average_price = (typical_price * dataframe['volume']).cumsum() / dataframe['volume'].cumsum()
1028
+ if not volume_weighted_average_price.empty and not pd.isna(volume_weighted_average_price.iloc[-1]):
1029
+ indicators['vwap'] = float(volume_weighted_average_price.iloc[-1])
1030
+ except Exception as error:
1031
+ print(f"⚠️ VWAP calculation failed for {timeframe}: {error}")
1032
+
1033
+ if len(dataframe) >= 14:
1034
+ rsi_series = ta.rsi(dataframe['close'], length=14)
1035
+ if rsi_series is not None and not rsi_series.empty and rsi_series.iloc[-1] is not np.nan:
1036
+ indicators['rsi'] = float(rsi_series.iloc[-1])
1037
+
1038
+ if len(dataframe) >= 26:
1039
+ macd = ta.macd(dataframe['close'])
1040
+ if macd is not None and not macd.empty:
1041
+ if 'MACDh_12_26_9' in macd.columns and macd['MACDh_12_26_9'].iloc[-1] is not np.nan:
1042
+ indicators['macd_hist'] = float(macd['MACDh_12_26_9'].iloc[-1])
1043
+ if 'MACD_12_26_9' in macd.columns and macd['MACD_12_26_9'].iloc[-1] is not np.nan:
1044
+ indicators['macd_line'] = float(macd['MACD_12_26_9'].iloc[-1])
1045
+ if 'MACDs_12_26_9' in macd.columns and macd['MACDs_12_26_9'].iloc[-1] is not np.nan:
1046
+ indicators['macd_signal'] = float(macd['MACDs_12_26_9'].iloc[-1])
1047
+
1048
+ if len(dataframe) >= 20:
1049
+ bollinger_bands = ta.bbands(dataframe['close'], length=20, std=2)
1050
+ if bollinger_bands is not None and not bollinger_bands.empty:
1051
+ if 'BBL_20_2.0' in bollinger_bands.columns and bollinger_bands['BBL_20_2.0'].iloc[-1] is not np.nan:
1052
+ indicators['bb_lower'] = float(bollinger_bands['BBL_20_2.0'].iloc[-1])
1053
+ if 'BBU_20_2.0' in bollinger_bands.columns and bollinger_bands['BBU_20_2.0'].iloc[-1] is not np.nan:
1054
+ indicators['bb_upper'] = float(bollinger_bands['BBU_20_2.0'].iloc[-1])
1055
+ if 'BBM_20_2.0' in bollinger_bands.columns and bollinger_bands['BBM_20_2.0'].iloc[-1] is not np.nan:
1056
+ indicators['bb_middle'] = float(bollinger_bands['BBM_20_2.0'].iloc[-1])
1057
+
1058
+ if len(dataframe) >= 14:
1059
+ average_true_range = ta.atr(high=dataframe['high'], low=dataframe['low'], close=dataframe['close'], length=14)
1060
+ if average_true_range is not None and not average_true_range.empty and average_true_range.iloc[-1] is not np.nan:
1061
+ indicators['atr'] = float(average_true_range.iloc[-1])
1062
+
1063
+ if len(dataframe) >= 26:
1064
+ ema_12 = ta.ema(dataframe['close'], length=12)
1065
+ ema_26 = ta.ema(dataframe['close'], length=26)
1066
+ if ema_12 is not None and not ema_12.empty and ema_12.iloc[-1] is not np.nan:
1067
+ indicators['ema_12'] = float(ema_12.iloc[-1])
1068
+ if ema_26 is not None and not ema_26.empty and ema_26.iloc[-1] is not np.nan:
1069
+ indicators['ema_26'] = float(ema_26.iloc[-1])
1070
+
1071
+ return indicators
1072
+
1073
+ def _normalize_features_corrected(self, features):
1074
+ """تطبيع الميزات بشكل صحيح"""
1075
+ normalized_features = {}
1076
+ for key, value in features.items():
1077
+ if value is None:
1078
+ normalized_features[key] = 0.0
1079
+ continue
1080
+ if key == 'rsi':
1081
+ normalized_features[key] = max(0, min(100, value))
1082
+ elif key in ['macd_hist', 'macd_line', 'macd_signal', 'vwap', 'atr']:
1083
+ normalized_features[key] = value
1084
+ elif 'ema' in key or 'bb_' in key:
1085
+ normalized_features[key] = value
1086
+ else:
1087
+ try:
1088
+ if abs(value) > 1000:
1089
+ normalized_features[key] = value / 1000
1090
+ else:
1091
+ normalized_features[key] = value
1092
+ except:
1093
+ normalized_features[key] = value
1094
+ return normalized_features
1095
+
1096
+ def _run_monte_carlo_simulation(self, dataframe, number_of_simulations=1000, number_of_steps=20):
1097
+ """تشغيل محاكاة مونت كارلو"""
1098
+ if dataframe.empty or len(dataframe) < 2:
1099
+ return 0.0
1100
+ log_returns = np.log(dataframe['close'] / dataframe['close'].shift(1)).dropna()
1101
+ if log_returns.empty:
1102
+ return 0.0
1103
+ mean_return = log_returns.mean()
1104
+ volatility = log_returns.std()
1105
+ initial_price = dataframe['close'].iloc[-1]
1106
+ success_count = 0
1107
+ for _ in range(number_of_simulations):
1108
+ random_values = np.random.normal(0, 1, number_of_steps)
1109
+ daily_returns = np.exp(mean_return - 0.5 * volatility**2 + volatility * random_values)
1110
+ simulated_prices = initial_price * daily_returns.cumprod()
1111
+ if (simulated_prices[-1] / initial_price) > 1.02:
1112
+ success_count += 1
1113
+ return success_count / number_of_simulations
1114
+
1115
+ def _calculate_initial_score(self, indicators, current_price, market_context):
1116
+ """حساب النقاط الأولية"""
1117
+ score = 0.5
1118
+ fast_timeframes = ['5m', '15m']
1119
+ for timeframe in fast_timeframes:
1120
+ timeframe_indicators = indicators.get(timeframe, {})
1121
+ if not timeframe_indicators:
1122
+ continue
1123
+ if 'rsi' in timeframe_indicators:
1124
+ rsi_value = timeframe_indicators['rsi']
1125
+ if isinstance(rsi_value, (int, float)):
1126
+ if rsi_value < 30:
1127
+ score += 0.2
1128
+ elif rsi_value < 40:
1129
+ score += 0.1
1130
+ elif rsi_value > 70:
1131
+ score -= 0.1
1132
+ if 'macd_hist' in timeframe_indicators and timeframe_indicators['macd_hist'] > 0:
1133
+ score += 0.15
1134
+ if all(key in timeframe_indicators for key in ['ema_12', 'ema_26']):
1135
+ if timeframe_indicators['ema_12'] > timeframe_indicators['ema_26']:
1136
+ score += 0.15
1137
+ slow_timeframes = ['1h', '4h', '1d']
1138
+ for timeframe in slow_timeframes:
1139
+ timeframe_indicators = indicators.get(timeframe, {})
1140
+ if not timeframe_indicators:
1141
+ continue
1142
+ if all(key in timeframe_indicators for key in ['ema_12', 'ema_26']):
1143
+ if timeframe_indicators['ema_12'] > timeframe_indicators['ema_26']:
1144
+ score += 0.10
1145
+ if all(key in timeframe_indicators for key in ['bb_upper', 'bb_lower']):
1146
+ if current_price > timeframe_indicators['bb_upper']:
1147
+ score += 0.10
1148
+ elif current_price <= timeframe_indicators['bb_lower']:
1149
+ score += 0.05
1150
+ if '5m' in indicators and 'vwap' in indicators['5m'] and current_price > indicators['5m']['vwap']:
1151
+ score += 0.10
1152
+ if market_context:
1153
+ bitcoin_sentiment = market_context.get('btc_sentiment')
1154
+ fear_greed_index = market_context.get('fear_and_greed_index', 50)
1155
+ if bitcoin_sentiment == 'BULLISH' and fear_greed_index > 60:
1156
+ score *= 1.2
1157
+ elif bitcoin_sentiment == 'BEARISH' or fear_greed_index < 30:
1158
+ score *= 0.8
1159
+ return min(max(score, 0.0), 1.0)
1160
+
1161
+ def _normalize_features(self, features):
1162
+ return self._normalize_features_corrected(features)
1163
+
1164
+ def _prepare_data_for_ml(self, all_indicators, current_price):
1165
+ feature_vector = []
1166
+ timeframes = ['5m', '15m', '1h', '4h', '1d']
1167
+ indicator_keys = ['rsi', 'macd_hist', 'macd_line', 'bb_upper', 'bb_lower', 'atr', 'ema_12', 'ema_26', 'vwap']
1168
+ for timeframe in timeframes:
1169
+ timeframe_indicators = all_indicators.get(timeframe, {})
1170
+ for key in indicator_keys:
1171
+ feature_vector.append(timeframe_indicators.get(key, 0.0))
1172
+ feature_vector.append(current_price)
1173
+ return feature_vector
1174
+
1175
+ def _calculate_liquidity_score(self, hourly_dataframe):
1176
+ if hourly_dataframe.empty:
1177
+ return 0.0
1178
+ hourly_dataframe['dollar_volume'] = hourly_dataframe['volume'] * hourly_dataframe['close']
1179
+ return float(hourly_dataframe['dollar_volume'].mean())
1180
+
1181
+ def _calculate_fibonacci_levels(self, daily_dataframe):
1182
+ """مهملة: استخدام _improve_fibonacci_levels بدلاً من ذلك"""
1183
+ return self._improve_fibonacci_levels(daily_dataframe, 0)
1184
+
1185
+ def classify_opportunity_type(self, indicators, current_price):
1186
+ fast_signals = 0
1187
+ slow_signals = 0
1188
+ for timeframe in ['5m', '15m']:
1189
+ timeframe_indicators = indicators.get(timeframe, {})
1190
+ if not timeframe_indicators:
1191
+ continue
1192
+ if timeframe_indicators.get('rsi', 100) < 35:
1193
+ fast_signals += 1
1194
+ if timeframe_indicators.get('macd_hist', 0) > 0:
1195
+ fast_signals += 1
1196
+ if all(key in timeframe_indicators for key in ['ema_12', 'ema_26']):
1197
+ if timeframe_indicators['ema_12'] > timeframe_indicators['ema_26']:
1198
+ fast_signals += 1
1199
+ if timeframe == '5m' and timeframe_indicators.get('vwap') and current_price > timeframe_indicators['vwap'] * 1.02:
1200
+ fast_signals += 1
1201
+ for timeframe in ['1h', '4h', '1d']:
1202
+ timeframe_indicators = indicators.get(timeframe, {})
1203
+ if not timeframe_indicators:
1204
+ continue
1205
+ if 40 <= timeframe_indicators.get('rsi', 50) <= 60:
1206
+ slow_signals += 1
1207
+ if all(key in timeframe_indicators for key in ['ema_12', 'ema_26']):
1208
+ if timeframe_indicators['ema_12'] > timeframe_indicators['ema_26']:
1209
+ slow_signals += 1
1210
+ if timeframe_indicators.get('bb_middle') and current_price > timeframe_indicators['bb_middle']:
1211
+ slow_signals += 1
1212
+ if fast_signals >= 3:
1213
+ return {
1214
+ "type": "FAST_PUMP", "timeframe": "15m-1h", "take_profit_multiplier": 1.08, "stop_loss_multiplier": 0.97,
1215
+ "confidence": min(fast_signals / 6.0, 1.0), "description": "فرصة صعود سريع قوية على الأطر الزمنية القصيرة"
1216
+ }
1217
+ elif slow_signals >= 3:
1218
+ return {
1219
+ "type": "SLOW_GROWTH", "timeframe": "4h-1d", "take_profit_multiplier": 1.05, "stop_loss_multiplier": 0.95,
1220
+ "confidence": min(slow_signals / 6.0, 1.0), "description": "فرصة نمو مستدام على الأطر الزمنية الطويلة"
1221
+ }
1222
+ return {
1223
+ "type": "NEUTRAL", "timeframe": "N/A", "take_profit_multiplier": 1.05, "stop_loss_multiplier": 0.95,
1224
+ "confidence": 0.3, "description": "لا توجد إشارات واضحة لنوع محدد من الفرص"
1225
+ }
1226
+
1227
+ def _calculate_whale_activity_score(self, whale_data):
1228
+ """حساب درجة نشاط الحيتان بناءً على البيانات الحقيقية فقط"""
1229
+ if not whale_data.get('data_available', False):
1230
+ return 0.0
1231
+
1232
+ total_transactions = whale_data.get('transfer_count', 0)
1233
+ total_volume = whale_data.get('total_volume', 0)
1234
+
1235
+ score = 0.0
1236
+ if total_transactions >= 10:
1237
+ score += 0.3
1238
+ elif total_transactions >= 5:
1239
+ score += 0.15
1240
+
1241
+ if total_volume > 500000:
1242
+ score += 0.2
1243
+ elif total_volume > 100000:
1244
+ score += 0.1
1245
+
1246
+ return min(score, 0.5)
1247
+
1248
+ def filter_top_candidates(self, candidates, number_of_candidates=10):
1249
+ """تصفية أفضل المرشحين"""
1250
+ valid_candidates = [candidate for candidate in candidates if candidate is not None]
1251
+ return sorted(valid_candidates, key=lambda candidate: candidate.get('enhanced_final_score', candidate.get('final_score', 0)), reverse=True)[:number_of_candidates]
1252
+
1253
+ # دوال المحاكاة المحلية للاستخدام كبديل
1254
+ def local_analyze_opportunity(candidate_data):
1255
+ """تحليل محسن مع مراعاة مخاطر RSI"""
1256
+ score = candidate_data.get('enhanced_final_score', candidate_data.get('final_score', 0))
1257
+ quality_warnings = candidate_data.get('quality_warnings', [])
1258
+
1259
+ # التحقق من تحذيرات RSI
1260
+ rsi_critical = any('🚨 RSI CRITICAL' in warning for warning in quality_warnings)
1261
+ rsi_warning = any('⚠️ RSI WARNING' in warning for warning in quality_warnings)
1262
+
1263
+ if rsi_critical:
1264
+ return {
1265
+ "action": "HOLD",
1266
+ "reasoning": "Local analysis: CRITICAL RSI levels detected - extreme overbought condition. High risk of correction.",
1267
+ "trade_type": "NONE",
1268
+ "stop_loss": None,
1269
+ "take_profit": None,
1270
+ "expected_target_minutes": 15,
1271
+ "confidence_level": 0.1,
1272
+ "model_source": "local_safety_filter",
1273
+ "strategy": "GENERIC"
1274
+ }
1275
+
1276
+ advanced_indicators = candidate_data.get('advanced_indicators', {})
1277
+ strategy_scores = candidate_data.get('strategy_scores', {})
1278
+
1279
+ if not advanced_indicators:
1280
+ return {
1281
+ "action": "HOLD",
1282
+ "reasoning": "Local analysis: Insufficient advanced indicator data.",
1283
+ "trade_type": "NONE",
1284
+ "stop_loss": None,
1285
+ "take_profit": None,
1286
+ "expected_target_minutes": 15,
1287
+ "confidence_level": 0.3,
1288
+ "model_source": "local",
1289
+ "strategy": "GENERIC"
1290
+ }
1291
+
1292
+ action = "HOLD"
1293
+ reasoning = "Local analysis: No strong buy signal based on enhanced rules."
1294
+ trade_type = "NONE"
1295
+ stop_loss = None
1296
+ take_profit = None
1297
+ expected_minutes = 15
1298
+ confidence = 0.3
1299
+
1300
+ five_minute_indicators = advanced_indicators.get('5m', {})
1301
+ one_hour_indicators = advanced_indicators.get('1h', {})
1302
+
1303
+ buy_conditions = 0
1304
+ total_conditions = 0
1305
+
1306
+ if isinstance(score, (int, float)) and score > 0.70:
1307
+ buy_conditions += 1
1308
+ total_conditions += 1
1309
+
1310
+ # شرط RSI أكثر تحفظاً
1311
+ rsi_five_minute = five_minute_indicators.get('rsi', 50)
1312
+ if 30 <= rsi_five_minute <= 65: # نطاق آمن لـ RSI
1313
+ buy_conditions += 1
1314
+ total_conditions += 1
1315
+
1316
+ if five_minute_indicators.get('macd_hist', 0) > 0:
1317
+ buy_conditions += 1
1318
+ total_conditions += 1
1319
+
1320
+ if (five_minute_indicators.get('ema_9', 0) > five_minute_indicators.get('ema_21', 0) and
1321
+ one_hour_indicators.get('ema_9', 0) > one_hour_indicators.get('ema_21', 0)):
1322
+ buy_conditions += 1
1323
+ total_conditions += 1
1324
+
1325
+ if five_minute_indicators.get('volume_ratio', 0) > 1.5:
1326
+ buy_conditions += 1
1327
+ total_conditions += 1
1328
+
1329
+ confidence = buy_conditions / total_conditions if total_conditions > 0 else 0.3
1330
+
1331
+ # خصم الثقة بناءً على تحذيرات RSI
1332
+ if rsi_warning:
1333
+ confidence *= 0.7 # خصم 30% للتحذيرات
1334
+ reasoning += " RSI warning applied."
1335
+
1336
+ if confidence >= 0.6:
1337
+ action = "BUY"
1338
+ current_price = candidate_data['current_price']
1339
+ trade_type = "LONG"
1340
+
1341
+ # وقف خسارة أكثر تحفظاً لـ RSI المرتفع
1342
+ if rsi_warning:
1343
+ stop_loss = current_price * 0.93 # 7% stop loss للتحذيرات
1344
+ else:
1345
+ stop_loss = current_price * 0.95 # 5% stop loss عادي
1346
+
1347
+ if 'bb_upper' in five_minute_indicators:
1348
+ take_profit = five_minute_indicators['bb_upper'] * 1.02
1349
+ else:
1350
+ take_profit = current_price * 1.05
1351
+
1352
+ if confidence >= 0.8:
1353
+ expected_minutes = 10
1354
+ elif confidence >= 0.6:
1355
+ expected_minutes = 18
1356
+ else:
1357
+ expected_minutes = 25
1358
+
1359
+ reasoning = f"Local enhanced analysis: Strong buy signal with {buy_conditions}/{total_conditions} conditions met. Confidence: {confidence:.2f}"
1360
+ if rsi_warning:
1361
+ reasoning += " (RSI warning - trading with caution)"
1362
+
1363
+ return {
1364
+ "action": action,
1365
+ "reasoning": reasoning,
1366
+ "trade_type": trade_type,
1367
+ "stop_loss": stop_loss,
1368
+ "take_profit": take_profit,
1369
+ "expected_target_minutes": expected_minutes,
1370
+ "confidence_level": confidence,
1371
+ "model_source": "local",
1372
+ "strategy": "GENERIC"
1373
+ }
1374
+
1375
+ def local_re_analyze_trade(trade_data, processed_data):
1376
+ current_price = processed_data['current_price']
1377
+ stop_loss = trade_data['stop_loss']
1378
+ take_profit = trade_data['take_profit']
1379
+ action = "HOLD"
1380
+ reasoning = "Local re-analysis: No significant change to trigger an update or close."
1381
+ if stop_loss and current_price <= stop_loss:
1382
+ action = "CLOSE_TRADE"
1383
+ reasoning = "Local re-analysis: Stop loss has been hit."
1384
+ elif take_profit and current_price >= take_profit:
1385
+ action = "CLOSE_TRADE"
1386
+ reasoning = "Local re-analysis: Take profit has been hit."
1387
+
1388
+ strategy = trade_data.get('strategy', 'GENERIC')
1389
+ if strategy == 'unknown':
1390
+ strategy = trade_data.get('decision_data', {}).get('strategy', 'GENERIC')
1391
+
1392
+ return {
1393
+ "action": action,
1394
+ "reasoning": reasoning,
1395
+ "new_stop_loss": None,
1396
+ "new_take_profit": None,
1397
+ "new_expected_minutes": None,
1398
+ "model_source": "local",
1399
+ "strategy": strategy
1400
+ }
1401
+
1402
+ print("✅ Enhanced ML System Loaded - Integrated with Learning Engine - REAL DATA ONLY - Optimized Strategy Scoring with Pattern Enhancement")
app (32).py ADDED
@@ -0,0 +1,953 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py - الإصدار المحدث مع إصلاح الأخطاء الحرجة
2
+ import os
3
+ import traceback
4
+ import signal
5
+ import sys
6
+ import uvicorn
7
+ import asyncio
8
+ from contextlib import asynccontextmanager
9
+ from fastapi import FastAPI, BackgroundTasks, HTTPException
10
+ from datetime import datetime, timedelta
11
+ from r2 import R2Service
12
+ from LLM import LLMService, local_analyze_opportunity, local_re_analyze_trade
13
+ from data_manager import DataManager
14
+ from ML import MLProcessor as FeatureProcessor
15
+ from learning_engine import LearningEngine
16
+ import time
17
+ import json
18
+ import state
19
+ import re
20
+
21
+ # إعدادات النظام
22
+ TOP_N_SYMBOLS = 100
23
+ OPPORTUNITY_COUNT = 10
24
+ CHUNK_SIZE = 5
25
+
26
+ # المتغيرات العامة للنظام
27
+ r2_service_global = None
28
+ data_manager_global = None
29
+ llm_service_global = None
30
+ learning_engine_global = None
31
+ realtime_monitor = None
32
+
33
+ # Real-time trade monitoring with enhanced risk management
34
+ class RealTimeTradeMonitor:
35
+ def __init__(self):
36
+ self.monitoring_tasks = {}
37
+ self.is_running = False
38
+
39
+ async def start_monitoring(self):
40
+ """بدء مراقبة جميع الصفقات المفتوحة"""
41
+ self.is_running = True
42
+ print("🔍 Starting real-time trade monitoring...")
43
+
44
+ while self.is_running:
45
+ try:
46
+ open_trades = await r2_service_global.get_open_trades_async()
47
+
48
+ for trade in open_trades:
49
+ symbol = trade['symbol']
50
+ if symbol not in self.monitoring_tasks:
51
+ asyncio.create_task(self._monitor_single_trade(trade))
52
+ self.monitoring_tasks[symbol] = trade
53
+
54
+ current_symbols = {trade['symbol'] for trade in open_trades}
55
+ for symbol in list(self.monitoring_tasks.keys()):
56
+ if symbol not in current_symbols:
57
+ del self.monitoring_tasks[symbol]
58
+
59
+ await asyncio.sleep(10)
60
+
61
+ except Exception as error:
62
+ print(f"❌ Real-time monitor error: {error}")
63
+ await asyncio.sleep(30)
64
+
65
+ async def _monitor_single_trade(self, trade):
66
+ """مراقبة صفقة فردية في الوقت الحقيقي"""
67
+ symbol = trade['symbol']
68
+ strategy = trade.get('strategy', 'GENERIC')
69
+ print(f"📊 Starting real-time monitoring for {symbol} (Strategy: {strategy})")
70
+
71
+ while symbol in self.monitoring_tasks and self.is_running:
72
+ try:
73
+ current_price = await data_manager_global.get_latest_price_async(symbol)
74
+ if not current_price:
75
+ await asyncio.sleep(15)
76
+ continue
77
+
78
+ entry_price = trade['entry_price']
79
+ stop_loss = trade.get('stop_loss')
80
+ take_profit = trade.get('take_profit')
81
+
82
+ should_close = False
83
+ close_reason = ""
84
+
85
+ if stop_loss and current_price <= stop_loss:
86
+ should_close = True
87
+ close_reason = f"Stop loss hit: {current_price} <= {stop_loss}"
88
+ elif take_profit and current_price >= take_profit:
89
+ should_close = True
90
+ close_reason = f"Take profit hit: {current_price} >= {take_profit}"
91
+
92
+ if not should_close and current_price > entry_price:
93
+ dynamic_stop = current_price * 0.98
94
+ if dynamic_stop > (stop_loss or 0):
95
+ trade['stop_loss'] = dynamic_stop
96
+ print(f"🔒 Updated trailing stop for {symbol}: {dynamic_stop:.4f}")
97
+
98
+ if should_close:
99
+ print(f"🚨 IMMEDIATE CLOSE: {symbol} - {close_reason} - Strategy: {strategy}")
100
+
101
+ if r2_service_global.acquire_lock():
102
+ try:
103
+ await r2_service_global.close_trade_async(trade, current_price)
104
+ print(f"✅ Trade {symbol} closed immediately at {current_price}. Strategy: {strategy}")
105
+
106
+ if learning_engine_global and learning_engine_global.initialized:
107
+ await learning_engine_global.analyze_trade_outcome(trade, 'CLOSED_BY_MONITOR')
108
+
109
+ asyncio.create_task(run_bot_cycle_async())
110
+
111
+ finally:
112
+ r2_service_global.release_lock()
113
+
114
+ if symbol in self.monitoring_tasks:
115
+ del self.monitoring_tasks[symbol]
116
+ break
117
+
118
+ await asyncio.sleep(15)
119
+
120
+ except Exception as error:
121
+ print(f"❌ Real-time monitoring error for {symbol}: {error}")
122
+ await asyncio.sleep(30)
123
+
124
+ def stop_monitoring(self):
125
+ """إيقاف جميع مهام المراقبة"""
126
+ self.is_running = False
127
+ self.monitoring_tasks.clear()
128
+ print("🛑 Real-time trade monitoring stopped")
129
+
130
+ async def monitor_market_async():
131
+ """Background task to continuously monitor market health"""
132
+ global data_manager_global
133
+
134
+ init_attempts = 0
135
+ while data_manager_global is None and init_attempts < 10:
136
+ print(f"⏳ Waiting for data manager initialization... (attempt {init_attempts + 1}/10)")
137
+ await asyncio.sleep(3)
138
+ init_attempts += 1
139
+
140
+ if data_manager_global is None:
141
+ print("❌ Data manager failed to initialize after 10 attempts")
142
+ return
143
+
144
+ while True:
145
+ try:
146
+ print("👁️ Monitoring market sentiment...")
147
+
148
+ try:
149
+ market_context = await data_manager_global.get_market_context_async()
150
+ except Exception as error:
151
+ print(f"⚠️ Failed to get market context: {error}")
152
+ market_context = await get_fallback_market_context()
153
+
154
+ if not market_context:
155
+ print("❌ Failed to get market context. Assuming neutral state.")
156
+ state.MARKET_STATE_OK = True
157
+ await asyncio.sleep(60)
158
+ continue
159
+
160
+ whale_analysis = market_context.get('general_whale_activity', {})
161
+ whale_sentiment = whale_analysis.get('sentiment', 'NEUTRAL')
162
+ is_critical = whale_analysis.get('critical_alert', False)
163
+ total_volume = whale_analysis.get('total_volume_usd', 0)
164
+
165
+ print(f"🐋 Whale Analysis: {whale_sentiment} | Critical: {is_critical} | Volume: ${total_volume:,.0f}")
166
+ print(f"📈 Whale Description: {whale_analysis.get('description', 'No data')}")
167
+
168
+ bitcoin_sentiment = market_context.get('btc_sentiment')
169
+ fear_greed_index = market_context.get('fear_and_greed_index')
170
+
171
+ should_halt_trading = False
172
+ halt_reason = ""
173
+
174
+ if is_critical:
175
+ should_halt_trading = True
176
+ halt_reason = f"CRITICAL whale activity detected: {whale_analysis.get('description')}"
177
+ elif bitcoin_sentiment == 'BEARISH' and (fear_greed_index is not None and fear_greed_index < 30):
178
+ should_halt_trading = True
179
+ halt_reason = f"Bearish market conditions (BTC: {bitcoin_sentiment}, F&G: {fear_greed_index})"
180
+
181
+ if should_halt_trading:
182
+ print(f"🚨🚨🚨 MARKET HALT: {halt_reason} 🚨🚨🚨")
183
+ state.MARKET_STATE_OK = False
184
+
185
+ try:
186
+ await r2_service_global.save_system_logs_async({
187
+ "market_halt": True,
188
+ "reason": halt_reason,
189
+ "whale_sentiment": whale_sentiment,
190
+ "is_critical": is_critical
191
+ })
192
+ except Exception as log_error:
193
+ print(f"⚠️ Failed to save market halt log: {log_error}")
194
+ else:
195
+ if not state.MARKET_STATE_OK:
196
+ print("🟢 Market conditions improved. Resuming normal operations.")
197
+ state.MARKET_STATE_OK = True
198
+
199
+ await asyncio.sleep(60)
200
+ except Exception as error:
201
+ print(f"❌ An error occurred during COMPREHENSIVE market monitoring: {error}")
202
+ traceback.print_exc()
203
+ state.MARKET_STATE_OK = True
204
+ await asyncio.sleep(60)
205
+
206
+ async def get_fallback_market_context():
207
+ """Fallback function when main market context fails"""
208
+ return {
209
+ 'timestamp': datetime.now().isoformat(),
210
+ 'general_whale_activity': {
211
+ 'sentiment': 'NEUTRAL',
212
+ 'description': 'Fallback mode - system initializing',
213
+ 'critical_alert': False,
214
+ 'transaction_count': 0,
215
+ 'total_volume_usd': 0
216
+ },
217
+ 'btc_sentiment': 'NEUTRAL',
218
+ 'fear_and_greed_index': 50
219
+ }
220
+
221
+ def safe_float_conversion(value, default=0.0):
222
+ """تحويل آمن للقيم إلى أرقام"""
223
+ try:
224
+ if value is None:
225
+ return default
226
+ if isinstance(value, (int, float)):
227
+ return float(value)
228
+ if isinstance(value, str):
229
+ cleaned = ''.join(character for character in value if character.isdigit() or character in '.-')
230
+ return float(cleaned) if cleaned else default
231
+ return default
232
+ except (ValueError, TypeError):
233
+ return default
234
+
235
+ async def validate_candidate_data_enhanced(candidate):
236
+ """✨ تحسين التحقق من جودة المرشحين"""
237
+ try:
238
+ required_fields = ['symbol', 'current_price', 'final_score', 'enhanced_final_score']
239
+
240
+ for field in required_fields:
241
+ if field not in candidate:
242
+ candidate[field] = 0.0 if field.endswith('_score') or field == 'current_price' else 'UNKNOWN'
243
+
244
+ candidate['current_price'] = safe_float_conversion(candidate.get('current_price'), 0.0)
245
+ candidate['final_score'] = safe_float_conversion(candidate.get('final_score'), 0.5)
246
+ candidate['enhanced_final_score'] = safe_float_conversion(candidate.get('enhanced_final_score'), candidate['final_score'])
247
+
248
+ if 'reasons_for_candidacy' not in candidate or not candidate['reasons_for_candidacy']:
249
+ candidate['reasons_for_candidacy'] = ['unknown_reason']
250
+
251
+ if 'sentiment_data' not in candidate or not candidate['sentiment_data']:
252
+ candidate['sentiment_data'] = {
253
+ 'btc_sentiment': 'NEUTRAL',
254
+ 'fear_and_greed_index': 50,
255
+ 'general_whale_activity': {'sentiment': 'NEUTRAL', 'critical_alert': False}
256
+ }
257
+
258
+ if 'advanced_indicators' not in candidate:
259
+ candidate['advanced_indicators'] = {}
260
+
261
+ if 'strategy_scores' not in candidate:
262
+ candidate['strategy_scores'] = {}
263
+
264
+ if 'recommended_strategy' not in candidate:
265
+ candidate['recommended_strategy'] = 'unknown'
266
+
267
+ # ✅ الإصلاح: التأكد من وجود استراتيجية مستهدفة صالحة
268
+ if 'target_strategy' not in candidate or not candidate['target_strategy'] or candidate['target_strategy'] == 'unknown':
269
+ candidate['target_strategy'] = 'GENERIC'
270
+
271
+ return True
272
+
273
+ except Exception as error:
274
+ print(f"❌ Failed to validate candidate data for {candidate.get('symbol')}: {error}")
275
+ return False
276
+
277
+ async def analyze_market_strategy(market_context):
278
+ """تحديد الاستراتيجية المثلى بناءً على ظروف السوق"""
279
+ try:
280
+ prompt = f"""
281
+ You are a professional crypto portfolio manager. Analyze the current market conditions and determine the most suitable strategy.
282
+
283
+ **Market Data:**
284
+ - BTC Price: {market_context.get('bitcoin_price_usd')}
285
+ - BTC Sentiment: {market_context.get('btc_sentiment')}
286
+ - Fear & Greed Index: {market_context.get('fear_and_greed_index')}
287
+ - Whale Analysis: {market_context.get('general_whale_activity', {}).get('sentiment')}
288
+ - Critical Whale Alert: {market_context.get('general_whale_activity', {}).get('critical_alert')}
289
+
290
+ **Available Strategies:**
291
+ 1. AGGRESSIVE_GROWTH - For strong bull markets.
292
+ 2. DEFENSIVE_GROWTH - For volatile or uncertain markets.
293
+ 3. CONSERVATIVE - For bearish or high-risk markets.
294
+ 4. HIGH_FREQUENCY - For sideways markets.
295
+ 5. WHALE_FOLLOWING - When whale activity is high and clear.
296
+ 6. GENERIC - Balanced approach for normal conditions.
297
+
298
+ **Required:**
299
+ - Choose one primary strategy.
300
+ - Explain why in a single sentence.
301
+ - Set an acceptable risk tolerance (1 to 10).
302
+ - Determine the optimal number of coins to scan (50 to 200).
303
+
304
+ **Output (JSON only):**
305
+ {{
306
+ "primary_strategy": "STRATEGY_NAME",
307
+ "reasoning": "Brief reasoning.",
308
+ "risk_tolerance": 5,
309
+ "optimal_scan_count": 100
310
+ }}
311
+ """
312
+
313
+ response = await llm_service_global._call_llm(prompt)
314
+
315
+ try:
316
+ json_match = re.search(r'\{.*\}', response, re.DOTALL)
317
+ strategy_data = json.loads(json_match.group())
318
+ except:
319
+ strategy_data = {
320
+ "primary_strategy": "GENERIC",
321
+ "reasoning": "Fallback strategy for market stability",
322
+ "risk_tolerance": 5,
323
+ "optimal_scan_count": 100,
324
+ }
325
+
326
+ return strategy_data
327
+
328
+ except Exception as error:
329
+ print(f"❌ Failed to analyze market strategy: {error}")
330
+ return {
331
+ "primary_strategy": "GENERIC",
332
+ "reasoning": "Fallback due to analysis error",
333
+ "risk_tolerance": 5,
334
+ "optimal_scan_count": 100,
335
+ }
336
+
337
+ async def find_strategy_specific_candidates(strategy, scan_count):
338
+ """✨ نظام فلترة ذكي يستخدم الاستراتيجيات المتخصصة - عتبات مخفضة"""
339
+ try:
340
+ # 1. جلب قائمة المرشحين الأولية
341
+ all_candidates = await data_manager_global.find_high_potential_candidates(scan_count * 2)
342
+
343
+ if not all_candidates:
344
+ print(f"⚠️ الماسح العام لم يجد أي مرشحين أوليين.")
345
+ return []
346
+
347
+ # 2. تحديث market_context قبل المعالجة
348
+ market_context = await data_manager_global.get_market_context_async()
349
+ if not market_context:
350
+ print("❌ Failed to get market context for strategy analysis")
351
+ return []
352
+
353
+ feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
354
+
355
+ processed_candidates = []
356
+ for candidate in all_candidates[:30]: # ⬇️ تخفيض من 50 إلى 30 للأداء
357
+ try:
358
+ # تحويل البيانات الخام إلى بيانات معالجة
359
+ symbol_with_reasons = [{'symbol': candidate['symbol'], 'reasons': candidate.get('reasons', [])}]
360
+ ohlcv_data = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
361
+
362
+ if ohlcv_data and ohlcv_data[0]:
363
+ # ✅ تحديث market_context قبل كل معالجة لمنع الخطأ
364
+ try:
365
+ updated_market_context = await data_manager_global.get_market_context_async()
366
+ if updated_market_context:
367
+ feature_processor.market_context = updated_market_context
368
+ except Exception as e:
369
+ print(f"⚠️ Failed to update market context for {candidate['symbol']}: {e}")
370
+ # الاستمرار بالسياق القديم إذا فشل التحديث
371
+
372
+ processed = await feature_processor.process_and_score_symbol_enhanced(ohlcv_data[0])
373
+ if processed:
374
+ processed_candidates.append(processed)
375
+ except Exception as e:
376
+ print(f"⚠️ فشل معالجة {candidate.get('symbol')}: {e}")
377
+
378
+ if not processed_candidates:
379
+ print("⚠️ لم يتم معالجة أي مرشح بنجاح")
380
+ return []
381
+
382
+ # 3. فرز المرشحين حسب الاستراتيجية المطلوبة
383
+ if strategy != 'GENERIC':
384
+ # تحديد أفضل المرشحين للاستراتيجية المحددة
385
+ strategy_candidates = []
386
+ for candidate in processed_candidates:
387
+ # ✅ استخدام الدرجات الأساسية بدلاً من المرجحة
388
+ base_scores = candidate.get('base_strategy_scores', {})
389
+ strategy_score = base_scores.get(strategy, 0)
390
+
391
+ # ✅ تخفيض كبير في عتبة القبول
392
+ if strategy_score > 0.2: # ⬇️ تخفيض من 0.4 إلى 0.2
393
+ candidate['strategy_match_score'] = strategy_score
394
+ strategy_candidates.append(candidate)
395
+ print(f"✅ {candidate['symbol']} مناسب لـ {strategy} (درجة: {strategy_score:.3f})")
396
+
397
+ # فرز حسب تطابق الاستراتيجية
398
+ sorted_candidates = sorted(strategy_candidates,
399
+ key=lambda x: x.get('strategy_match_score', 0),
400
+ reverse=True)
401
+ top_candidates = sorted_candidates[:15] # ⬇️ تخفيض من 20 إلى 15
402
+
403
+ print(f"✅ تم اختيار {len(top_candidates)} مرشحًا لاستراتيجية {strategy}")
404
+ else:
405
+ # للاستراتيجية العامة، استخدم النقاط المحسنة
406
+ sorted_candidates = sorted(processed_candidates,
407
+ key=lambda x: x.get('enhanced_final_score', 0),
408
+ reverse=True)
409
+ top_candidates = sorted_candidates[:15] # ⬇️ تخفيض من 20 إلى 15
410
+ print(f"✅ تم اختيار {len(top_candidates)} مرشحًا للاستراتيجية العامة")
411
+
412
+ return top_candidates
413
+
414
+ except Exception as error:
415
+ print(f"❌ فشل في نظام الفلترة المتقدم: {error}")
416
+ traceback.print_exc()
417
+ return []
418
+
419
+ async def find_new_opportunities_async():
420
+ """✨ NEW: المسح المحسن باستراتيجية مسبقة مع عتبات مخفضة"""
421
+ print("🔍 Scanning for new opportunities with reduced thresholds...")
422
+ try:
423
+ await r2_service_global.save_system_logs_async({
424
+ "opportunity_scan_started": True, "timestamp": datetime.now().isoformat()
425
+ })
426
+
427
+ print("🧠 Determining trading strategy...")
428
+ market_context = await data_manager_global.get_market_context_async()
429
+ if not market_context:
430
+ print("❌ Failed to fetch market context. Cannot determine strategy.")
431
+ return
432
+
433
+ strategy_decision = await analyze_market_strategy(market_context)
434
+
435
+ print(f"🎯 Selected Strategy: {strategy_decision['primary_strategy']}")
436
+ print(f"📝 Reasoning: {strategy_decision['reasoning']}")
437
+ print(f"⚡ Risk Tolerance: {strategy_decision.get('risk_tolerance', 5)}/10")
438
+ print(f"🔍 Optimal Scan Count: {strategy_decision.get('optimal_scan_count', 100)}")
439
+
440
+ print(f"🔍 Finding top candidates using dynamic ranking...")
441
+ high_potential_candidates = await find_strategy_specific_candidates(
442
+ strategy_decision['primary_strategy'],
443
+ strategy_decision.get('optimal_scan_count', 100)
444
+ )
445
+
446
+ if not high_potential_candidates:
447
+ print("🔄 لا توجد مرشحين متخصصين، جلب مرشحين عامين...")
448
+ # ✅ استرجاع مرشحين عامين كبديل
449
+ high_potential_candidates = await data_manager_global.find_high_potential_candidates(20)
450
+ if high_potential_candidates:
451
+ for candidate in high_potential_candidates:
452
+ candidate['target_strategy'] = 'GENERIC'
453
+ print(f"✅ تم تحميل {len(high_potential_candidates)} مرشح عام")
454
+ else:
455
+ print("✅ No new candidates found after dynamic ranking.")
456
+ await r2_service_global.save_system_logs_async({
457
+ "no_candidates_found": True, "strategy": strategy_decision['primary_strategy'],
458
+ "reason": "Scanner did not return any initial candidates."
459
+ })
460
+ return
461
+
462
+ all_processed_candidates = []
463
+ for index in range(0, len(high_potential_candidates), CHUNK_SIZE):
464
+ chunk = high_potential_candidates[index:index+CHUNK_SIZE]
465
+
466
+ chunk_data = await data_manager_global.get_fast_pass_data_async(chunk)
467
+
468
+ print(f"⏳ Processing and scoring chunk {index//CHUNK_SIZE + 1}...")
469
+ # ✅ تحديث market_context قبل معالجة كل شريحة
470
+ updated_market_context = await data_manager_global.get_market_context_async()
471
+ if not updated_market_context:
472
+ updated_market_context = market_context # استخدام السياق القديم كبديل
473
+
474
+ feature_processor = FeatureProcessor(updated_market_context, data_manager_global, learning_engine_global)
475
+
476
+ processed_chunk = await asyncio.gather(*[
477
+ feature_processor.process_and_score_symbol_enhanced(data) for data in chunk_data
478
+ ])
479
+ all_processed_candidates.extend([c for c in processed_chunk if c is not None])
480
+
481
+ await asyncio.sleep(1)
482
+
483
+ if not all_processed_candidates:
484
+ print("❌ No candidates were processed successfully.")
485
+ return
486
+
487
+ # ✅ استخدام السياق المحدث للتصفية النهائية
488
+ updated_market_context = await data_manager_global.get_market_context_async()
489
+ if not updated_market_context:
490
+ updated_market_context = market_context
491
+
492
+ feature_processor = FeatureProcessor(updated_market_context, data_manager_global, learning_engine_global)
493
+ top_candidates = feature_processor.filter_top_candidates(all_processed_candidates, OPPORTUNITY_COUNT)
494
+
495
+ print(f"✅ Identified {len(top_candidates)} top candidates after final scoring.")
496
+
497
+ await r2_service_global.save_candidates_data_async(
498
+ candidates_data=top_candidates,
499
+ reanalysis_data={"strategy_used": strategy_decision, "market_conditions": market_context}
500
+ )
501
+
502
+ if not top_candidates:
503
+ print("❌ No strong candidates left after final filtering.")
504
+ await r2_service_global.save_system_logs_async({
505
+ "no_strong_candidates": True, "strategy": strategy_decision['primary_strategy'],
506
+ "initial_candidates_count": len(high_potential_candidates)
507
+ })
508
+ return
509
+
510
+ print("🧠 Getting LLM analysis for top candidates...")
511
+
512
+ for candidate in top_candidates:
513
+ try:
514
+ if not await validate_candidate_data_enhanced(candidate):
515
+ print(f"⚠️ Skipping {candidate.get('symbol')} due to quality issues")
516
+ continue
517
+
518
+ llm_analysis_data = await llm_service_global.get_trading_decision(candidate)
519
+
520
+ if not llm_analysis_data:
521
+ print(f"⚠️ LLM analysis failed for {candidate['symbol']}. Moving to next.")
522
+ continue
523
+
524
+ if llm_analysis_data.get('action') == "HOLD":
525
+ print(f"🧠 LLM decided to HOLD on {candidate['symbol']}. Moving to next.")
526
+ continue
527
+
528
+ if llm_analysis_data.get('action') in ["BUY", "SELL"]:
529
+ # ✅ التحقق النهائي من الاستراتيجية
530
+ final_strategy = llm_analysis_data.get('strategy')
531
+ candidate_strategy = candidate.get('target_strategy', 'GENERIC')
532
+
533
+ # إذا كانت استراتيجية LLM غير صالحة، استخدم استراتيجية المرشح
534
+ if not final_strategy or final_strategy == 'unknown' or final_strategy == 'GENERIC':
535
+ final_strategy = candidate_strategy
536
+ llm_analysis_data['strategy'] = final_strategy
537
+ print(f"🔧 تصحيح استراتيجية LLM لـ {candidate['symbol']}: {final_strategy}")
538
+
539
+ print(f"🎯 الاستراتيجية النهائية: {final_strategy}")
540
+
541
+ print("\n========================================================")
542
+ print(f"💎💎💎 New Trading Opportunity Identified! 💎💎💎")
543
+ print(f" Symbol: {candidate['symbol']}")
544
+ print(f" Action: {llm_analysis_data.get('action')}")
545
+ print(f" Strategy: {final_strategy}")
546
+ print(f" Reasoning: {llm_analysis_data.get('reasoning')}")
547
+ print(f" Confidence: {llm_analysis_data.get('confidence_level')}")
548
+ print("========================================================\n")
549
+
550
+ await r2_service_global.save_system_logs_async({
551
+ "new_opportunity_found": True, "symbol": candidate['symbol'],
552
+ "action": llm_analysis_data.get('action'), "strategy": final_strategy,
553
+ "confidence": llm_analysis_data.get('confidence_level', 0)
554
+ })
555
+
556
+ return {
557
+ "symbol": candidate['symbol'],
558
+ "decision": llm_analysis_data,
559
+ "current_price": candidate['current_price'],
560
+ "strategy": final_strategy
561
+ }
562
+
563
+ except Exception as error:
564
+ print(f"❌ LLM/Fallback error for {candidate.get('symbol', 'unknown')}: {error}")
565
+ traceback.print_exc()
566
+
567
+ print("✅ Cycle finished. No actionable BUY/SELL opportunities found by LLM.")
568
+ return None
569
+
570
+ except Exception as error:
571
+ print(f"❌ An error occurred while scanning for opportunities: {error}")
572
+ traceback.print_exc()
573
+ await r2_service_global.save_system_logs_async({"opportunity_scan_error": True, "error": str(error)})
574
+ return None
575
+
576
+ async def re_analyze_open_trade_async(trade_data):
577
+ """Re-analyzes an open trade with enhanced strategy preservation"""
578
+ symbol = trade_data.get('symbol')
579
+
580
+ try:
581
+ entry_time = datetime.fromisoformat(trade_data['entry_timestamp'])
582
+ current_time = datetime.now()
583
+ hold_minutes = (current_time - entry_time).total_seconds() / 60
584
+
585
+ print(f"⏳ Re-analyzing trade: {symbol} (held for {hold_minutes:.1f} minutes)")
586
+
587
+ # ✅ الإصلاح المحسن: الحفاظ على الاستراتيجية الأصلية مع التحقق الشامل
588
+ original_strategy = trade_data.get('strategy')
589
+ if not original_strategy or original_strategy == 'unknown':
590
+ original_strategy = trade_data.get('decision_data', {}).get('strategy', 'GENERIC')
591
+ print(f"🔧 Fixed original strategy for {symbol}: {original_strategy}")
592
+
593
+ reanalysis_context = {
594
+ 'trade_data': {
595
+ 'symbol': trade_data.get('symbol'),
596
+ 'entry_price': trade_data.get('entry_price'),
597
+ 'entry_time': trade_data.get('entry_timestamp'),
598
+ 'hold_minutes': hold_minutes,
599
+ 'strategy': original_strategy
600
+ }
601
+ }
602
+
603
+ try:
604
+ market_context = await data_manager_global.get_market_context_async()
605
+ except Exception as error:
606
+ print(f"⚠️ Failed to get market context: {error}. Using basic market data...")
607
+ market_context = {'btc_sentiment': 'NEUTRAL'}
608
+
609
+ symbol_with_reasons = [{'symbol': symbol, 'reasons': ['re-analysis']}]
610
+
611
+ ohlcv_data_list = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
612
+ if not ohlcv_data_list:
613
+ print(f"❌ Failed to fetch latest data for {symbol}.")
614
+ return None
615
+
616
+ raw_data = ohlcv_data_list[0]
617
+ # ✅ تحديث market_context قبل المعالجة
618
+ try:
619
+ updated_market_context = await data_manager_global.get_market_context_async()
620
+ if updated_market_context:
621
+ market_context = updated_market_context
622
+ except Exception as e:
623
+ print(f"⚠️ Failed to update market context for re-analysis: {e}")
624
+
625
+ feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
626
+ processed_data = await feature_processor.process_and_score_symbol(raw_data)
627
+
628
+ if not processed_data:
629
+ print(f"❌ Failed to process latest data for {symbol}.")
630
+ return None
631
+
632
+ await r2_service_global.save_candidates_data_async(
633
+ candidates_data=None,
634
+ reanalysis_data={'market_context': market_context, 'processed_data': processed_data}
635
+ )
636
+
637
+ print(f"🧠 Getting LLM re-analysis for {symbol}...")
638
+ try:
639
+ re_analysis_decision = await llm_service_global.re_analyze_trade_async(trade_data, processed_data)
640
+ source = re_analysis_decision.get('model_source', 'LLM')
641
+ except Exception as error:
642
+ print(f"❌ LLM re-analysis error: {error}. Falling back to local.")
643
+ re_analysis_decision = local_re_analyze_trade(trade_data, processed_data)
644
+ source = 'local_fallback'
645
+
646
+ final_decision = _apply_patience_logic(re_analysis_decision, hold_minutes, trade_data, processed_data)
647
+
648
+ # ✅ الإصلاح النهائي: التأكد من وجود الاستراتيجية في القرار النهائي
649
+ if not final_decision.get('strategy') or final_decision['strategy'] == 'unknown':
650
+ final_decision['strategy'] = original_strategy
651
+ print(f"🔧 Final re-analysis strategy fix for {symbol}: {original_strategy}")
652
+
653
+ print(f"✅ Re-analysis decision for {symbol}: {final_decision.get('action')}. Strategy: {final_decision.get('strategy')}. Source: {source}")
654
+
655
+ await r2_service_global.save_system_logs_async({
656
+ "trade_reanalyzed": True, "symbol": symbol, "action": final_decision.get('action'),
657
+ "hold_minutes": hold_minutes, "source": source, "strategy": final_decision.get('strategy')
658
+ })
659
+
660
+ return {
661
+ "symbol": symbol, "decision": final_decision,
662
+ "current_price": processed_data.get('current_price'), "hold_minutes": hold_minutes
663
+ }
664
+
665
+ except Exception as error:
666
+ print(f"❌ An error occurred during trade re-analysis: {error}")
667
+ traceback.print_exc()
668
+ await r2_service_global.save_system_logs_async({"reanalysis_error": True, "symbol": symbol, "error": str(error)})
669
+ return None
670
+
671
+ def _apply_patience_logic(decision, hold_minutes, trade_data, processed_data):
672
+ """Apply patience logic to prevent premature selling decisions"""
673
+ action = decision.get('action')
674
+
675
+ if action == "CLOSE_TRADE" and hold_minutes < 20:
676
+ current_price = processed_data.get('current_price', 0)
677
+ entry_price = trade_data.get('entry_price', 0)
678
+
679
+ try:
680
+ profit_loss_percent = ((current_price - entry_price) / entry_price) * 100
681
+ except (TypeError, ZeroDivisionError):
682
+ profit_loss_percent = 0
683
+
684
+ if profit_loss_percent < 2:
685
+ print(f"🛑 Blocked premature selling! Only {hold_minutes:.1f} minutes held, PnL: {profit_loss_percent:.2f}%")
686
+ decision['action'] = "HOLD"
687
+ decision['reasoning'] = f"Patience Filter: Blocked premature sell. Held for {hold_minutes:.1f}m. Giving trade more time."
688
+ return decision
689
+
690
+ return decision
691
+
692
+ async def run_bot_cycle_async():
693
+ """The main asynchronous bot cycle with enhanced strategy validation"""
694
+ print(f"\n{'='*70}")
695
+ print(f"⏳ New cycle initiated at: {datetime.now().isoformat()}")
696
+ print(f"{'='*70}")
697
+
698
+ try:
699
+ await r2_service_global.save_system_logs_async({"cycle_started": True})
700
+
701
+ if not r2_service_global.acquire_lock():
702
+ print("❌ Failed to acquire lock. Skipping cycle.")
703
+ return
704
+
705
+ open_trades = []
706
+ try:
707
+ open_trades = await r2_service_global.get_open_trades_async()
708
+ print(f"✅ Found {len(open_trades)} open trade(s).")
709
+
710
+ # ✅ الإصلاح المحسن: فحص وإصلاح الاستراتيجيات الفارغة في الصفقات المفتوحة
711
+ trades_fixed = 0
712
+ for trade in open_trades:
713
+ if not trade.get('strategy') or trade['strategy'] == 'unknown':
714
+ original_strategy = trade.get('decision_data', {}).get('strategy', 'GENERIC')
715
+ trade['strategy'] = original_strategy
716
+ trades_fixed += 1
717
+ print(f"🔧 Fixed missing strategy for {trade['symbol']}: {trade['strategy']}")
718
+
719
+ if trades_fixed > 0:
720
+ print(f"✅ Fixed strategies for {trades_fixed} trades.")
721
+ await r2_service_global.save_open_trades_async(open_trades)
722
+
723
+ should_look_for_new_trade = not open_trades
724
+
725
+ if open_trades:
726
+ now = datetime.now()
727
+ trades_to_reanalyze = [
728
+ trade for trade in open_trades
729
+ if now >= datetime.fromisoformat(trade.get('expected_target_time', now.isoformat()))
730
+ ]
731
+
732
+ if trades_to_reanalyze:
733
+ print(f"🔍 Re-analyzing {len(trades_to_reanalyze)} trade(s)...")
734
+ for trade in trades_to_reanalyze:
735
+ result = await re_analyze_open_trade_async(trade)
736
+ if result and result['decision'].get('action') == "CLOSE_TRADE":
737
+ await r2_service_global.close_trade_async(trade, result['current_price'])
738
+ print(f"✅ Trade for {trade['symbol']} CLOSED. Strategy: {trade.get('strategy', 'unknown')}")
739
+ if learning_engine_global and learning_engine_global.initialized:
740
+ trade_with_strategy = trade.copy()
741
+ strategy = result['decision'].get('strategy', trade.get('strategy', 'GENERIC'))
742
+ trade_with_strategy['strategy'] = strategy
743
+ await learning_engine_global.analyze_trade_outcome(trade_with_strategy, 'CLOSED_BY_REANALYSIS')
744
+ should_look_for_new_trade = True
745
+ elif result and result['decision'].get('action') == "UPDATE_TRADE":
746
+ await r2_service_global.update_trade_async(trade, result['decision'])
747
+ print(f"✅ Trade for {trade['symbol']} UPDATED. Strategy: {trade.get('strategy', 'unknown')}")
748
+ else:
749
+ print(f"✅ Trade for {trade['symbol']} is on HOLD. Strategy: {trade.get('strategy', 'unknown')}")
750
+ else:
751
+ print("✅ No trades due for re-analysis yet.")
752
+
753
+ if should_look_for_new_trade:
754
+ portfolio_state = await r2_service_global.get_portfolio_state_async()
755
+ if portfolio_state.get("current_capital_usd", 0) > 1:
756
+ print(f"✅ Capital available (${portfolio_state['current_capital_usd']:.2f}). Scanning...")
757
+ new_opportunity = await find_new_opportunities_async()
758
+ if new_opportunity:
759
+ print(f"✅ Opportunity for {new_opportunity['symbol']} confirmed! Saving trade. Strategy: {new_opportunity.get('strategy')}")
760
+
761
+ # ✅ التحقق النهائي قبل الحفظ
762
+ if not new_opportunity['decision'].get('strategy'):
763
+ new_opportunity['decision']['strategy'] = new_opportunity.get('strategy', 'GENERIC')
764
+ print(f"🔧 Final pre-save strategy fix: {new_opportunity['decision']['strategy']}")
765
+
766
+ await r2_service_global.save_new_trade_async(
767
+ new_opportunity['symbol'],
768
+ new_opportunity['decision'],
769
+ new_opportunity['current_price']
770
+ )
771
+ newly_opened_trades = await r2_service_global.get_open_trades_async()
772
+ for trade in newly_opened_trades:
773
+ if trade['symbol'] == new_opportunity['symbol']:
774
+ asyncio.create_task(realtime_monitor._monitor_single_trade(trade))
775
+ break
776
+ else:
777
+ print("✅ Scan complete. No actionable opportunities identified.")
778
+ else:
779
+ print("😴 No available capital. Waiting for current trade to close.")
780
+
781
+ finally:
782
+ print("✅ Cycle finished. Releasing lock.")
783
+ r2_service_global.release_lock()
784
+ await r2_service_global.save_system_logs_async({"cycle_completed": True, "open_trades": len(open_trades)})
785
+
786
+ except Exception as error:
787
+ print(f"❌ Unhandled error in main cycle: {error}")
788
+ traceback.print_exc()
789
+ await r2_service_global.save_system_logs_async({"cycle_error": True, "error": str(error)})
790
+ if r2_service_global.lock_acquired:
791
+ r2_service_global.release_lock()
792
+
793
+ @asynccontextmanager
794
+ async def lifespan(application: FastAPI):
795
+ global r2_service_global, data_manager_global, llm_service_global, learning_engine_global, realtime_monitor
796
+ print("===== Application Startup =====")
797
+
798
+ try:
799
+ r2_service_global = R2Service()
800
+ llm_service_global = LLMService()
801
+ contracts_database = await r2_service_global.load_contracts_db_async()
802
+
803
+ data_manager_global = DataManager(contracts_database)
804
+ await data_manager_global.initialize()
805
+
806
+ # ✅ تهيئة نظام التعلم مع تمرير data_manager
807
+ learning_engine_global = LearningEngine(r2_service_global, data_manager_global)
808
+ await learning_engine_global.initialize_enhanced() # ✅ استخدام التهيئة المحسنة
809
+
810
+ # ✅ إجبار تحديث الاستراتيجيات من البيانات الحالية
811
+ await learning_engine_global.force_strategy_learning()
812
+
813
+ # ✅ التحقق من أن الأوزان يتم تحميلها
814
+ if learning_engine_global.initialized:
815
+ weights = await learning_engine_global.get_optimized_strategy_weights("bull_market")
816
+ print(f"🎯 الأوزان المحملة: {weights}")
817
+
818
+ realtime_monitor = RealTimeTradeMonitor()
819
+
820
+ asyncio.create_task(monitor_market_async())
821
+ asyncio.create_task(realtime_monitor.start_monitoring())
822
+
823
+ await r2_service_global.save_system_logs_async({"application_started": True})
824
+
825
+ print("\n✅ All services initialized. Application is ready.\n")
826
+ yield
827
+
828
+ except Exception as error:
829
+ print(f"❌ Application startup failed: {error}")
830
+ traceback.print_exc()
831
+ if r2_service_global:
832
+ await r2_service_global.save_system_logs_async({"application_startup_failed": True, "error": str(error)})
833
+ raise
834
+ finally:
835
+ await cleanup_on_shutdown()
836
+
837
+ application = FastAPI(lifespan=lifespan)
838
+
839
+ @application.get("/run-cycle")
840
+ async def run_cycle_api():
841
+ """API endpoint to trigger the bot cycle."""
842
+ asyncio.create_task(run_bot_cycle_async())
843
+ return {"message": "Bot cycle initiated in the background."}
844
+
845
+ @application.get("/health")
846
+ async def health_check():
847
+ """Detailed health check."""
848
+ learning_metrics = {}
849
+ if learning_engine_global and learning_engine_global.initialized:
850
+ learning_metrics = await learning_engine_global.calculate_performance_metrics()
851
+
852
+ return {
853
+ "status": "healthy",
854
+ "timestamp": datetime.now().isoformat(),
855
+ "services": {
856
+ "r2_service": "initialized" if r2_service_global else "uninitialized",
857
+ "llm_service": "initialized" if llm_service_global else "uninitialized",
858
+ "data_manager": "initialized" if data_manager_global else "uninitialized",
859
+ "learning_engine": "active" if learning_engine_global and learning_engine_global.initialized else "inactive",
860
+ "realtime_monitor": "running" if realtime_monitor and realtime_monitor.is_running else "stopped"
861
+ },
862
+ "market_state_ok": state.MARKET_STATE_OK,
863
+ "learning_engine": learning_metrics
864
+ }
865
+
866
+ @application.get("/stats")
867
+ async def get_performance_stats():
868
+ """Get performance statistics for all services."""
869
+ try:
870
+ market_context = await data_manager_global.get_market_context_async() if data_manager_global else {}
871
+
872
+ learning_stats = {}
873
+ improvement_suggestions = []
874
+ if learning_engine_global and learning_engine_global.initialized:
875
+ learning_stats = await learning_engine_global.calculate_performance_metrics()
876
+ improvement_suggestions = await learning_engine_global.suggest_improvements()
877
+
878
+ stats = {
879
+ "timestamp": datetime.now().isoformat(),
880
+ "data_manager": data_manager_global.get_performance_stats() if data_manager_global else {},
881
+ "market_state": {
882
+ "is_healthy": state.MARKET_STATE_OK,
883
+ "description": "Market is healthy for trading" if state.MARKET_STATE_OK else "Market conditions are unfavorable",
884
+ "context": market_context
885
+ },
886
+ "realtime_monitoring": {
887
+ "active_trades": len(realtime_monitor.monitoring_tasks) if realtime_monitor else 0,
888
+ "is_running": realtime_monitor.is_running if realtime_monitor else False
889
+ },
890
+ "learning_engine": learning_stats,
891
+ "improvement_suggestions": improvement_suggestions
892
+ }
893
+ return stats
894
+ except Exception as error:
895
+ raise HTTPException(status_code=500, detail=f"Failed to retrieve stats: {str(error)}")
896
+
897
+ @application.get("/logs/status")
898
+ async def get_logs_status():
899
+ """Get status of logging system."""
900
+ try:
901
+ open_trades = await r2_service_global.get_open_trades_async()
902
+ portfolio_state = await r2_service_global.get_portfolio_state_async()
903
+
904
+ return {
905
+ "logging_system": "active",
906
+ "open_trades_count": len(open_trades),
907
+ "current_capital": portfolio_state.get("current_capital_usd", 0),
908
+ "total_trades": portfolio_state.get("total_trades", 0),
909
+ "timestamp": datetime.now().isoformat()
910
+ }
911
+ except Exception as error:
912
+ raise HTTPException(status_code=500, detail=f"Failed to get logs status: {str(error)}")
913
+
914
+ async def cleanup_on_shutdown():
915
+ """Cleanup function for graceful shutdown."""
916
+ global r2_service_global, data_manager_global, realtime_monitor, learning_engine_global
917
+ print("\n🛑 Shutdown signal received. Cleaning up...")
918
+
919
+ if r2_service_global:
920
+ try:
921
+ await r2_service_global.save_system_logs_async({"application_shutdown": True})
922
+ except Exception as log_error:
923
+ print(f"⚠️ Failed to save shutdown log: {log_error}")
924
+
925
+ if learning_engine_global and learning_engine_global.initialized:
926
+ try:
927
+ await learning_engine_global.save_weights_to_r2()
928
+ await learning_engine_global.save_performance_history()
929
+ print("✅ Learning engine data saved.")
930
+ except Exception as e:
931
+ print(f"⚠️ Failed to save learning engine data: {e}")
932
+
933
+ if realtime_monitor:
934
+ realtime_monitor.stop_monitoring()
935
+
936
+ if r2_service_global and r2_service_global.lock_acquired:
937
+ r2_service_global.release_lock()
938
+ print("✅ Lock released.")
939
+ if data_manager_global:
940
+ await data_manager_global.close()
941
+ print("✅ Cleanup completed.")
942
+
943
+ def signal_handler(signum, frame):
944
+ """Handle shutdown signals."""
945
+ print(f"\n⚠️ Received signal {signum}")
946
+ asyncio.create_task(cleanup_on_shutdown())
947
+ sys.exit(0)
948
+
949
+ signal.signal(signal.SIGINT, signal_handler)
950
+ signal.signal(signal.SIGTERM, signal_handler)
951
+
952
+ if __name__ == "__main__":
953
+ uvicorn.run(application, host="0.0.0.0", port=7860)
data_manager (23).py ADDED
@@ -0,0 +1,1431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # data_manager_fixed.py - الإصدار النهائي مع إصلاح الأخطاء الحرجة
2
+ import os, asyncio, httpx, json, traceback, backoff, re, time, hashlib
3
+ from datetime import datetime, timedelta
4
+ from functools import wraps
5
+ import ccxt.pro as ccxt
6
+ from ccxt.base.errors import RateLimitExceeded, DDoSProtection, NetworkError
7
+ import pandas as pd
8
+ import numpy as np
9
+ from state import MARKET_STATE_OK
10
+
11
+ # --- 🐋 نظام مراقبة الحيتان العام مع مصادر مؤكدة ---
12
+ class ReliableWhaleMonitor:
13
+ def __init__(self, contracts_db=None):
14
+ self._http_client = None
15
+ self.contracts_db = contracts_db or {}
16
+
17
+ self.whale_threshold_usd = 50000
18
+ self.token_whale_threshold_usd = 25000
19
+
20
+ # ✅ مصادر بيانات مؤكدة وعاملة 100%
21
+ self.reliable_data_sources = {
22
+ 'coin_gecko': 'https://api.coingecko.com/api/v3',
23
+ 'dex_screener': 'https://api.dexscreener.com/latest/dex',
24
+ 'fear_greed': 'https://api.alternative.me/fng/',
25
+ 'binance_api': 'https://api.binance.com/api/v3',
26
+ 'bybit_api': 'https://api.bybit.com/v2/public'
27
+ }
28
+
29
+ # ✅ RPC endpoints لكل الشبكات الرئيسية
30
+ self.rpc_endpoints = {
31
+ 'ethereum': [
32
+ 'https://eth-mainnet.public.blastapi.io',
33
+ 'https://rpc.ankr.com/eth',
34
+ 'https://cloudflare-eth.com'
35
+ ],
36
+ 'bsc': [
37
+ 'https://bsc-dataseed.binance.org/',
38
+ 'https://bsc-dataseed1.defibit.io/',
39
+ 'https://bsc-dataseed1.ninicoin.io/'
40
+ ],
41
+ 'polygon': [
42
+ 'https://polygon-rpc.com/',
43
+ 'https://rpc-mainnet.maticvigil.com/',
44
+ 'https://rpc.ankr.com/polygon'
45
+ ],
46
+ 'arbitrum': [
47
+ 'https://arb1.arbitrum.io/rpc',
48
+ 'https://arbitrum.public-rpc.com'
49
+ ],
50
+ 'optimism': [
51
+ 'https://mainnet.optimism.io',
52
+ 'https://optimism.public-rpc.com'
53
+ ],
54
+ 'avalanche': [
55
+ 'https://api.avax.network/ext/bc/C/rpc',
56
+ 'https://avalanche.public-rpc.com'
57
+ ],
58
+ 'fantom': [
59
+ 'https://rpc.ftm.tools/',
60
+ 'https://fantom.public-rpc.com'
61
+ ],
62
+ 'base': [
63
+ 'https://mainnet.base.org',
64
+ 'https://base.public-rpc.com'
65
+ ]
66
+ }
67
+
68
+ self.current_rpc_index = {network: 0 for network in self.rpc_endpoints.keys()}
69
+
70
+ # ✅ عناوين التبادلات الرئيسية للمراقبة
71
+ self.major_exchanges = {
72
+ 'binance': [
73
+ '0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be',
74
+ '0xd551234ae421e3bcba99a0da6d736074f22192ff',
75
+ '0x564286362092d8e7936f0549571a803b203aaced'
76
+ ],
77
+ 'coinbase': [
78
+ '0xa910f92acdaf488fa6ef02174fb86208ad7722ba',
79
+ '0x4fabb145d64652a948d72533023f6e7a623c7c53'
80
+ ],
81
+ 'kraken': [
82
+ '0x2910543af39aba0cd09dbb2d50200b3e800a63d2',
83
+ '0x689c56aef474df92d44a1b70850f808488f9769c'
84
+ ]
85
+ }
86
+
87
+ self.price_cache = {}
88
+ self.volume_cache = {}
89
+ self.whale_activity_cache = {}
90
+
91
+ @property
92
+ def http_client(self):
93
+ if self._http_client is None:
94
+ self._http_client = httpx.AsyncClient(timeout=20.0)
95
+ return self._http_client
96
+
97
+ async def close(self):
98
+ """إغلاق جميع العملاء بشكل آمن"""
99
+ if self._http_client:
100
+ await self._http_client.aclose()
101
+ self._http_client = None
102
+
103
+ async def _get_working_rpc(self, network):
104
+ """الحصول على RPC عامل للشبكة المطلوبة"""
105
+ if network not in self.rpc_endpoints:
106
+ return None
107
+
108
+ endpoints = self.rpc_endpoints[network]
109
+ start_index = self.current_rpc_index[network]
110
+
111
+ for i in range(len(endpoints)):
112
+ index = (start_index + i) % len(endpoints)
113
+ endpoint = endpoints[index]
114
+
115
+ try:
116
+ # اختبار اتصال RPC
117
+ payload = {
118
+ "jsonrpc": "2.0",
119
+ "method": "eth_blockNumber",
120
+ "params": [],
121
+ "id": 1
122
+ }
123
+
124
+ async with httpx.AsyncClient(timeout=10) as client:
125
+ response = await client.post(endpoint, json=payload)
126
+ if response.status_code == 200:
127
+ self.current_rpc_index[network] = index
128
+ return endpoint
129
+ except Exception:
130
+ continue
131
+
132
+ return None
133
+
134
+ async def _get_token_contract_from_coingecko(self, symbol):
135
+ """جلب عقد العملة من CoinGecko"""
136
+ try:
137
+ url = f"https://api.coingecko.com/api/v3/coins/{symbol.lower()}"
138
+ async with httpx.AsyncClient(timeout=10) as client:
139
+ response = await client.get(url)
140
+ if response.status_code == 200:
141
+ data = response.json()
142
+ platforms = data.get('platforms', {})
143
+
144
+ # إرجاع كل المنصات المتاحة
145
+ contract_info = {}
146
+ for platform, address in platforms.items():
147
+ if address and address != "":
148
+ # تحويل أسماء المنصات إلى أسماء شبكات قياسية
149
+ network_map = {
150
+ 'ethereum': 'ethereum',
151
+ 'binance-smart-chain': 'bsc',
152
+ 'polygon-pos': 'polygon',
153
+ 'arbitrum-one': 'arbitrum',
154
+ 'optimistic-ethereum': 'optimism',
155
+ 'avalanche': 'avalanche',
156
+ 'fantom': 'fantom',
157
+ 'base': 'base'
158
+ }
159
+ network = network_map.get(platform, platform)
160
+ contract_info[network] = {
161
+ 'address': address,
162
+ 'network': network,
163
+ 'symbol': symbol.upper(),
164
+ 'name': data.get('name', ''),
165
+ 'coingecko_id': data.get('id', '')
166
+ }
167
+
168
+ return contract_info if contract_info else None
169
+
170
+ except Exception as e:
171
+ print(f"⚠️ فشل جلب عقد {symbol} من CoinGecko: {e}")
172
+
173
+ return None
174
+
175
+ async def _get_reliable_price(self, coin_id):
176
+ """جلب سعر موثوق من CoinGecko"""
177
+ now = time.time()
178
+ cache_key = f"price_{coin_id}"
179
+
180
+ if cache_key in self.price_cache and (now - self.price_cache[cache_key]['timestamp']) < 300:
181
+ return self.price_cache[cache_key]['price']
182
+
183
+ try:
184
+ url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies=usd"
185
+ async with httpx.AsyncClient(timeout=10) as client:
186
+ response = await client.get(url)
187
+ if response.status_code == 200:
188
+ data = response.json()
189
+ price = data.get(coin_id, {}).get('usd')
190
+ if price and price > 0:
191
+ self.price_cache[cache_key] = {'price': price, 'timestamp': now}
192
+ return price
193
+ elif response.status_code == 429:
194
+ print("⚠️ CoinGecko rate limit reached, using fallback")
195
+ return None
196
+ except Exception as e:
197
+ print(f"⚠️ فشل جلب سعر {coin_id}: {e}")
198
+
199
+ return None
200
+
201
+ async def _get_btc_price(self):
202
+ """جلب سعر البيتكوين"""
203
+ return await self._get_reliable_price('bitcoin')
204
+
205
+ async def _get_eth_price(self):
206
+ """جلب سعر الإيثيريوم"""
207
+ return await self._get_reliable_price('ethereum')
208
+
209
+ async def _get_top_crypto_volumes(self):
210
+ """جلب أحجام التداول للعملات الرائدة من CoinGecko"""
211
+ try:
212
+ url = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=volume_desc&per_page=20&page=1&sparkline=false"
213
+ async with httpx.AsyncClient(timeout=10) as client:
214
+ response = await client.get(url)
215
+ if response.status_code == 200:
216
+ coins = response.json()
217
+
218
+ volume_data = []
219
+ for coin in coins:
220
+ if isinstance(coin, dict): # ✅ التحقق من النوع
221
+ volume_data.append({
222
+ 'symbol': coin.get('symbol', '').upper(),
223
+ 'name': coin.get('name', ''),
224
+ 'volume_24h': coin.get('total_volume', 0),
225
+ 'price': coin.get('current_price', 0),
226
+ 'price_change_24h': coin.get('price_change_percentage_24h', 0)
227
+ })
228
+
229
+ return volume_data
230
+ elif response.status_code == 429:
231
+ print("⚠️ CoinGecko rate limit reached for volumes")
232
+ return [] # إرجاع قائمة فارغة بدلاً من None
233
+ else:
234
+ print(f"⚠️ CoinGecko returned status {response.status_code}")
235
+ return [] # ✅ إرجاع قائمة فارغة بدلاً من None
236
+
237
+ except Exception as e:
238
+ print(f"⚠️ فشل جلب أحجام التداول: {e}")
239
+ return [] # ✅ إرجاع قائمة فارغة بدلاً من None
240
+
241
+ async def _get_dex_screener_data(self, token_address=None):
242
+ """جلب بيانات DEX من DexScreener (مصدر موثوق)"""
243
+ try:
244
+ if token_address:
245
+ url = f"https://api.dexscreener.com/latest/dex/tokens/{token_address}"
246
+ else:
247
+ url = "https://api.dexscreener.com/latest/dex/search?q=ETH"
248
+
249
+ async with httpx.AsyncClient(timeout=15) as client:
250
+ response = await client.get(url)
251
+ if response.status_code == 200:
252
+ return response.json()
253
+ except Exception as e:
254
+ print(f"⚠️ فشل جلب بيانات DexScreener: {e}")
255
+
256
+ return None
257
+
258
+ async def _analyze_dex_whale_activity(self):
259
+ """تحليل نشاط الحيتان على DEXs باستخدام DexScreener"""
260
+ print("🔍 تحليل نشاط الحيتان على DEXs...")
261
+
262
+ whale_activity = []
263
+
264
+ try:
265
+ # الحصول على بيانات ETH كمرجع رئيسي
266
+ eth_data = await self._get_dex_screener_data("0x2170ed0880ac9a755fd29b2688956bd959f933f8")
267
+
268
+ if eth_data and 'pairs' in eth_data:
269
+ for pair in eth_data['pairs'][:15]: # أول 15 زوج
270
+ try:
271
+ volume_24h = float(pair.get('volume', {}).get('h24', 0))
272
+ liquidity = float(pair.get('liquidity', {}).get('usd', 0))
273
+
274
+ # اعتبار التداولات الكبيرة كحركات حيتان
275
+ if volume_24h >= self.whale_threshold_usd:
276
+ whale_activity.append({
277
+ 'protocol': 'DEX',
278
+ 'symbol': pair.get('baseToken', {}).get('symbol', 'UNKNOWN'),
279
+ 'volume_usd': volume_24h,
280
+ 'liquidity_usd': liquidity,
281
+ 'price': float(pair.get('priceUsd', 0)),
282
+ 'exchange': pair.get('dexId', 'UNKNOWN'),
283
+ 'price_change_24h': float(pair.get('priceChange', {}).get('h24', 0))
284
+ })
285
+ except (ValueError, TypeError) as e:
286
+ continue
287
+
288
+ print(f"✅ تم تحليل {len(whale_activity)} حركة على DEXs")
289
+
290
+ except Exception as e:
291
+ print(f"❌ خطأ في تحليل DEXs: {e}")
292
+
293
+ return whale_activity
294
+
295
+ async def _analyze_cex_whale_activity(self):
296
+ """تحليل نشاط الحيتان على التبادلات المركزية باستخدام CCXT"""
297
+ print("🏦 تحليل نشاط الحيتان على CEXs...")
298
+
299
+ whale_activity = []
300
+
301
+ try:
302
+ # استخدام KuCoin كمثال (يعمل بدون API key للبيانات العامة)
303
+ exchange = ccxt.kucoin()
304
+ markets = await exchange.fetch_markets()
305
+
306
+ # تحليل أكبر 20 سوق من حيث الحجم
307
+ for market in markets[:20]:
308
+ try:
309
+ symbol = market['symbol']
310
+ if '/USDT' in symbol or '/BTC' in symbol:
311
+ ticker = await exchange.fetch_ticker(symbol)
312
+ volume_24h = ticker.get('quoteVolume', 0)
313
+
314
+ if volume_24h >= self.whale_threshold_usd:
315
+ whale_activity.append({
316
+ 'protocol': 'CEX',
317
+ 'symbol': symbol,
318
+ 'volume_usd': volume_24h,
319
+ 'price': ticker.get('last', 0),
320
+ 'price_change_24h': ticker.get('percentage', 0),
321
+ 'exchange': 'kucoin'
322
+ })
323
+ except Exception:
324
+ continue
325
+
326
+ print(f"✅ تم تحليل {len(whale_activity)} حركة على CEXs")
327
+
328
+ except Exception as e:
329
+ print(f"⚠️ فشل تحليل CEXs: {e}")
330
+
331
+ return whale_activity
332
+
333
+ async def _get_market_sentiment_analysis(self):
334
+ """تحليل مشاعر السوق بناءً على بيانات متعددة"""
335
+ print("🎭 تحليل مشاعر السوق...")
336
+
337
+ try:
338
+ # مؤشر الخوف والجشع
339
+ async with httpx.AsyncClient(timeout=10) as client:
340
+ response = await client.get("https://api.alternative.me/fng/")
341
+ if response.status_code == 200:
342
+ data = response.json()
343
+ fear_greed = int(data['data'][0]['value'])
344
+ fear_greed_class = data['data'][0]['value_classification']
345
+ else:
346
+ fear_greed = 50
347
+ fear_greed_class = "Neutral"
348
+
349
+ # اتجاه السوق بناءً على سعر البيتكوين
350
+ btc_price = await self._get_btc_price()
351
+ if btc_price:
352
+ if btc_price > 60000:
353
+ market_trend = "BULLISH"
354
+ elif btc_price < 55000:
355
+ market_trend = "BEARISH"
356
+ else:
357
+ market_trend = "NEUTRAL"
358
+ else:
359
+ market_trend = "NEUTRAL"
360
+
361
+ return {
362
+ 'fear_greed_index': fear_greed,
363
+ 'fear_greed_class': fear_greed_class,
364
+ 'market_trend': market_trend,
365
+ 'btc_price': btc_price
366
+ }
367
+
368
+ except Exception as e:
369
+ print(f"⚠️ فشل تحليل المشاعر: {e}")
370
+ return {
371
+ 'fear_greed_index': 50,
372
+ 'fear_greed_class': "Neutral",
373
+ 'market_trend': "NEUTRAL",
374
+ 'btc_price': None
375
+ }
376
+
377
+ async def get_comprehensive_whale_analysis(self):
378
+ """تحليل شامل للحيتان باستخدام مصادر موثوقة"""
379
+ print("🐋 بدء التحليل الشامل للحيتان...")
380
+
381
+ try:
382
+ # ✅ تشغيل جميع التحليلات بالتوازي
383
+ dex_task = self._analyze_dex_whale_activity()
384
+ cex_task = self._analyze_cex_whale_activity()
385
+ sentiment_task = self._get_market_sentiment_analysis()
386
+ volumes_task = self._get_top_crypto_volumes()
387
+
388
+ results = await asyncio.gather(
389
+ dex_task, cex_task, sentiment_task, volumes_task,
390
+ return_exceptions=True
391
+ )
392
+
393
+ # ✅ معالجة النتائج
394
+ dex_whales = results[0] if not isinstance(results[0], Exception) else []
395
+ cex_whales = results[1] if not isinstance(results[1], Exception) else []
396
+ sentiment = results[2] if not isinstance(results[2], Exception) else {}
397
+ top_volumes = results[3] if not isinstance(results[3], Exception) else []
398
+
399
+ # ✅ دمج جميع البيانات
400
+ all_whale_activity = dex_whales + cex_whales
401
+
402
+ # ✅ حساب الإحصائيات
403
+ total_volume = sum(activity.get('volume_usd', 0) for activity in all_whale_activity)
404
+ total_transactions = len(all_whale_activity)
405
+
406
+ # ✅ تحليل أحجام العملات الرائدة
407
+ high_volume_coins = [coin for coin in top_volumes if coin.get('volume_24h', 0) > 100000000] # 100M+
408
+
409
+ # ✅ حساب التأثير الشامل
410
+ overall_impact = self._calculate_overall_impact(
411
+ total_volume, total_transactions, len(high_volume_coins), sentiment
412
+ )
413
+
414
+ # ✅ تحليل النوايا
415
+ intent_analysis = self._analyze_trading_intents(all_whale_activity, top_volumes)
416
+
417
+ # ✅ إنشاء التقرير
418
+ description = self._generate_comprehensive_description(
419
+ overall_impact, total_volume, total_transactions,
420
+ len(high_volume_coins), intent_analysis, sentiment
421
+ )
422
+
423
+ print(f"📊 التحليل الشامل: {overall_impact}")
424
+ print(f"💰 الحجم الإجمالي: ${total_volume:,.0f}")
425
+ print(f"🔢 عدد المعاملات: {total_transactions}")
426
+ print(f"🎯 العملات عالية الحجم: {len(high_volume_coins)}")
427
+
428
+ return {
429
+ 'overall_impact': overall_impact,
430
+ 'total_volume_usd': total_volume,
431
+ 'total_transactions': total_transactions,
432
+ 'description': description,
433
+ 'high_volume_coins_count': len(high_volume_coins),
434
+ 'whale_activity': all_whale_activity[:10], # أهم 10 حركة
435
+ 'market_sentiment': sentiment,
436
+ 'trading_intents': intent_analysis,
437
+ 'timestamp': datetime.now().isoformat(),
438
+ 'data_available': len(all_whale_activity) > 0,
439
+ 'data_sources': ['coin_gecko', 'dex_screener', 'kucoin_api', 'fear_greed']
440
+ }
441
+
442
+ except Exception as e:
443
+ print(f"❌ فشل التحليل الشامل: {e}")
444
+ traceback.print_exc()
445
+ return self._get_fallback_analysis()
446
+
447
+ def _calculate_overall_impact(self, total_volume, total_transactions, high_volume_coins_count, sentiment):
448
+ """حساب التأثير الشامل"""
449
+ if total_volume == 0 and total_transactions == 0:
450
+ return 'LOW'
451
+
452
+ # ✅ تحليل الحجم
453
+ volume_score = 0
454
+ if total_volume > 10000000:
455
+ volume_score = 4
456
+ elif total_volume > 5000000:
457
+ volume_score = 3
458
+ elif total_volume > 1000000:
459
+ volume_score = 2
460
+ elif total_volume > 500000:
461
+ volume_score = 1
462
+
463
+ # ✅ تحليل النشاط
464
+ activity_score = 0
465
+ if total_transactions > 15:
466
+ activity_score = 3
467
+ elif total_transactions > 8:
468
+ activity_score = 2
469
+ elif total_transactions > 3:
470
+ activity_score = 1
471
+
472
+ # ✅ تحليل العملات عالية الحجم
473
+ volume_coins_score = min(high_volume_coins_count, 3)
474
+
475
+ # ✅ تحليل المشاعر
476
+ sentiment_score = 0
477
+ market_trend = sentiment.get('market_trend', 'NEUTRAL')
478
+ if market_trend == 'BULLISH':
479
+ sentiment_score = 1
480
+ elif market_trend == 'BEARISH':
481
+ sentiment_score = -1
482
+
483
+ total_score = volume_score + activity_score + volume_coins_score + sentiment_score
484
+
485
+ if total_score >= 6:
486
+ return 'HIGH'
487
+ elif total_score >= 3:
488
+ return 'MEDIUM'
489
+ else:
490
+ return 'LOW'
491
+
492
+ def _analyze_trading_intents(self, whale_activity, top_volumes):
493
+ """تحليل نوايا التداول"""
494
+ try:
495
+ # تحليل اتجاهات الأسعار في الحركات الكبيرة
496
+ bullish_count = 0
497
+ bearish_count = 0
498
+
499
+ for activity in whale_activity:
500
+ price_change = activity.get('price_change_24h', 0)
501
+ if price_change > 0:
502
+ bullish_count += 1
503
+ elif price_change < 0:
504
+ bearish_count += 1
505
+
506
+ # تحليل العملات عالية الحجم
507
+ volume_bullish = 0
508
+ volume_bearish = 0
509
+
510
+ for coin in top_volumes[:10]: # أهم 10 عملة
511
+ price_change = coin.get('price_change_24h', 0)
512
+ if price_change > 0:
513
+ volume_bullish += 1
514
+ elif price_change < 0:
515
+ volume_bearish += 1
516
+
517
+ total_bullish = bullish_count + volume_bullish
518
+ total_bearish = bearish_count + volume_bearish
519
+
520
+ if total_bullish > total_bearish:
521
+ dominant_intent = 'BUY'
522
+ buy_sell_ratio = total_bullish / max(total_bearish, 1)
523
+ elif total_bearish > total_bullish:
524
+ dominant_intent = 'SELL'
525
+ buy_sell_ratio = total_bullish / max(total_bearish, 1)
526
+ else:
527
+ dominant_intent = 'HOLD'
528
+ buy_sell_ratio = 1.0
529
+
530
+ # تحديد مشاعر السوق
531
+ if buy_sell_ratio > 1.5:
532
+ market_sentiment = 'BULLISH'
533
+ elif buy_sell_ratio > 1.0:
534
+ market_sentiment = 'SLIGHTLY_BULLISH'
535
+ elif buy_sell_ratio == 1.0:
536
+ market_sentiment = 'NEUTRAL'
537
+ elif buy_sell_ratio > 0.5:
538
+ market_sentiment = 'SLIGHTLY_BEARISH'
539
+ else:
540
+ market_sentiment = 'BEARISH'
541
+
542
+ return {
543
+ 'dominant_intent': dominant_intent,
544
+ 'buy_sell_ratio': buy_sell_ratio,
545
+ 'market_sentiment': market_sentiment,
546
+ 'bullish_signals': total_bullish,
547
+ 'bearish_signals': total_bearish
548
+ }
549
+
550
+ except Exception as e:
551
+ print(f"⚠️ خطأ في تحليل النوايا: {e}")
552
+ return {
553
+ 'dominant_intent': 'UNKNOWN',
554
+ 'buy_sell_ratio': 1.0,
555
+ 'market_sentiment': 'NEUTRAL',
556
+ 'bullish_signals': 0,
557
+ 'bearish_signals': 0
558
+ }
559
+
560
+ def _generate_comprehensive_description(self, overall_impact, total_volume, total_transactions, high_volume_coins_count, intent_analysis, sentiment):
561
+ """إنشاء وصف شامل"""
562
+ if overall_impact == 'HIGH':
563
+ base_desc = "🚨 نشاط تداول عالي الخطورة"
564
+ elif overall_impact == 'MEDIUM':
565
+ base_desc = "⚠️ نشاط تداول متوسط"
566
+ else:
567
+ base_desc = "✅ نشاط تداول طبيعي"
568
+
569
+ # ✅ إضافة تفاصيل الحجم والنشاط
570
+ base_desc += f" (حجم: ${total_volume:,.0f}, معاملات: {total_transactions}"
571
+
572
+ if high_volume_coins_count > 0:
573
+ base_desc += f", عملات عالية الحجم: {high_volume_coins_count}"
574
+
575
+ base_desc += ")"
576
+
577
+ # ✅ إضافة تحليل النوايا
578
+ dominant_intent = intent_analysis.get('dominant_intent', 'UNKNOWN')
579
+ market_sentiment = intent_analysis.get('market_sentiment', 'NEUTRAL')
580
+
581
+ if dominant_intent != 'UNKNOWN':
582
+ base_desc += f" - النوايا: {dominant_intent}"
583
+
584
+ # ✅ إضافة مؤشر المشاعر
585
+ sentiment_emoji = {
586
+ 'BULLISH': '📈',
587
+ 'SLIGHTLY_BULLISH': '↗️',
588
+ 'NEUTRAL': '➡️',
589
+ 'SLIGHTLY_BEARISH': '↘️',
590
+ 'BEARISH': '📉'
591
+ }.get(market_sentiment, '➡️')
592
+
593
+ base_desc += f" {sentiment_emoji}"
594
+
595
+ return base_desc
596
+
597
+ def _get_fallback_analysis(self):
598
+ """تحليل احتياطي عند فشل المصادر"""
599
+ return {
600
+ 'overall_impact': 'LOW',
601
+ 'total_volume_usd': 0,
602
+ 'total_transactions': 0,
603
+ 'description': 'بيانات الحيتان غير متاحة حاليًا',
604
+ 'high_volume_coins_count': 0,
605
+ 'whale_activity': [],
606
+ 'market_sentiment': {
607
+ 'fear_greed_index': 50,
608
+ 'fear_greed_class': 'Neutral',
609
+ 'market_trend': 'NEUTRAL'
610
+ },
611
+ 'trading_intents': {
612
+ 'dominant_intent': 'UNKNOWN',
613
+ 'buy_sell_ratio': 1.0,
614
+ 'market_sentiment': 'NEUTRAL'
615
+ },
616
+ 'timestamp': datetime.now().isoformat(),
617
+ 'data_available': False,
618
+ 'data_sources': []
619
+ }
620
+
621
+ async def get_whale_activity_legacy(self):
622
+ """وظيفة legacy للحفاظ على التوافق"""
623
+ analysis = await self.get_comprehensive_whale_analysis()
624
+
625
+ sentiment_map = {
626
+ 'BULLISH': 'BULLISH',
627
+ 'SLIGHTLY_BULLISH': 'BULLISH',
628
+ 'NEUTRAL': 'NEUTRAL',
629
+ 'SLIGHTLY_BEARISH': 'BEARISH',
630
+ 'BEARISH': 'BEARISH'
631
+ }
632
+
633
+ sentiment = sentiment_map.get(
634
+ analysis['trading_intents'].get('market_sentiment', 'NEUTRAL'),
635
+ 'NEUTRAL'
636
+ )
637
+
638
+ critical_alert = analysis['overall_impact'] == 'HIGH'
639
+
640
+ return {
641
+ 'data_available': analysis['data_available'],
642
+ 'description': analysis['description'],
643
+ 'critical_alert': critical_alert,
644
+ 'sentiment': sentiment,
645
+ 'total_volume_usd': analysis['total_volume_usd'],
646
+ 'transaction_count': analysis['total_transactions'],
647
+ 'market_impact': analysis['overall_impact'],
648
+ 'buy_sell_ratio': analysis['trading_intents'].get('buy_sell_ratio', 0.5),
649
+ 'whale_intent_analysis': analysis['trading_intents'],
650
+ 'scan_summary': {
651
+ 'data_sources_used': analysis.get('data_sources', []),
652
+ 'high_volume_coins': analysis.get('high_volume_coins_count', 0),
653
+ 'time_range_minutes': 60
654
+ }
655
+ }
656
+
657
+ # --- 🎯 نظام مراقبة الحيتان الخاص بالعملات المحددة ---
658
+ class SpecificWhaleMonitor:
659
+ """نظام مراقبة الحيتان الخاص بالعملات المحددة"""
660
+
661
+ def __init__(self, contracts_db, general_monitor):
662
+ self.contracts_db = contracts_db
663
+ self.general_monitor = general_monitor
664
+ self.specific_activity_cache = {}
665
+
666
+ async def get_specific_whale_activity(self, symbols):
667
+ """جلب تحركات الحيتان الخاصة برموز محددة"""
668
+ specific_activity = {}
669
+
670
+ for symbol in symbols:
671
+ try:
672
+ # تنظيف الرمز (إزالة /USDT etc.)
673
+ clean_symbol = symbol.replace('/USDT', '').replace('/BUSD', '').split('/')[0]
674
+
675
+ # البحث في قاعدة البيانات أولاً
676
+ contract_info = self.contracts_db.get(clean_symbol.upper())
677
+
678
+ if not contract_info:
679
+ # إذا لم يوجد، جلب من CoinGecko
680
+ contract_info = await self.general_monitor._get_token_contract_from_coingecko(clean_symbol)
681
+ if contract_info:
682
+ # حفظ في قاعدة البيانات للمستقبل
683
+ self.contracts_db[clean_symbol.upper()] = contract_info
684
+
685
+ if contract_info:
686
+ whale_data = await self._analyze_specific_whale_activity(clean_symbol, contract_info)
687
+ if whale_data:
688
+ specific_activity[symbol] = whale_data
689
+
690
+ except Exception as e:
691
+ print(f"⚠️ فشل مراقبة الحيتان الخاصة لـ {symbol}: {e}")
692
+ continue
693
+
694
+ return specific_activity
695
+
696
+ async def _analyze_specific_whale_activity(self, symbol, contract_info):
697
+ """ت��ليل تحركات الحيتان لعملة محددة"""
698
+ try:
699
+ whale_activity = {
700
+ 'symbol': symbol,
701
+ 'contract_info': contract_info,
702
+ 'large_transactions': [],
703
+ 'dex_activity': [],
704
+ 'total_volume_24h': 0,
705
+ 'whale_transaction_count': 0,
706
+ 'last_updated': datetime.now().isoformat()
707
+ }
708
+
709
+ # 1. تحليل نشاط DEX للعملة المحددة
710
+ dex_activity = await self._get_dex_activity_for_token(contract_info)
711
+ if dex_activity:
712
+ whale_activity['dex_activity'] = dex_activity
713
+ whale_activity['total_volume_24h'] = sum(tx.get('volume_usd', 0) for tx in dex_activity)
714
+ whale_activity['whale_transaction_count'] = len(dex_activity)
715
+
716
+ # 2. تحليل التحركات على الشبكة (إذا كان لدينا عقد)
717
+ for network, contract_data in contract_info.items():
718
+ if isinstance(contract_data, dict) and 'address' in contract_data:
719
+ network_activity = await self._get_whale_transactions_from_network(
720
+ contract_data['address'], network
721
+ )
722
+ whale_activity['large_transactions'].extend(network_activity)
723
+
724
+ return whale_activity if whale_activity['whale_transaction_count'] > 0 else None
725
+
726
+ except Exception as e:
727
+ print(f"⚠️ فشل تحليل الحيتان الخاصة لـ {symbol}: {e}")
728
+ return None
729
+
730
+ async def _get_dex_activity_for_token(self, contract_info):
731
+ """جلب نشاط DEX للعملة المحددة"""
732
+ dex_activity = []
733
+
734
+ try:
735
+ # استخدام أول عقد متاح للبحث في DexScreener
736
+ for network, contract_data in contract_info.items():
737
+ if isinstance(contract_data, dict) and 'address' in contract_data:
738
+ dex_data = await self.general_monitor._get_dex_screener_data(contract_data['address'])
739
+
740
+ if dex_data and 'pairs' in dex_data:
741
+ for pair in dex_data['pairs'][:5]: # أول 5 أزواج فقط
742
+ volume_24h = float(pair.get('volume', {}).get('h24', 0))
743
+
744
+ if volume_24h >= self.general_monitor.token_whale_threshold_usd:
745
+ dex_activity.append({
746
+ 'dex': pair.get('dexId', 'UNKNOWN'),
747
+ 'pair': pair.get('pairAddress', ''),
748
+ 'volume_usd': volume_24h,
749
+ 'price': float(pair.get('priceUsd', 0)),
750
+ 'price_change_24h': float(pair.get('priceChange', {}).get('h24', 0)),
751
+ 'liquidity_usd': float(pair.get('liquidity', {}).get('usd', 0)),
752
+ 'network': network
753
+ })
754
+ break # استخدام أول شبكة تعمل
755
+
756
+ except Exception as e:
757
+ print(f"⚠️ فشل جلب نشاط DEX الخاص: {e}")
758
+
759
+ return dex_activity
760
+
761
+ async def _get_whale_transactions_from_network(self, contract_address, network):
762
+ """جلب تحركات الحيتان من الشبكة باستخدام RPC"""
763
+ try:
764
+ rpc_url = await self.general_monitor._get_working_rpc(network)
765
+ if not rpc_url:
766
+ return []
767
+
768
+ # هنا يمكن إضافة كود لتحليل التحركات الكبيرة من الشبكة
769
+ # هذا مثال مبسط - في الواقع تحتاج لخدمات متخصصة مثل Moralis, Alchemy, etc.
770
+
771
+ payload = {
772
+ "jsonrpc": "2.0",
773
+ "method": "eth_getLogs",
774
+ "params": [{
775
+ "address": contract_address,
776
+ "fromBlock": "latest",
777
+ "toBlock": "latest",
778
+ "topics": [
779
+ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" # Transfer event
780
+ ]
781
+ }],
782
+ "id": 1
783
+ }
784
+
785
+ async with httpx.AsyncClient(timeout=15) as client:
786
+ response = await client.post(rpc_url, json=payload)
787
+ if response.status_code == 200:
788
+ data = response.json()
789
+ # معالجة البيانات هنا
790
+ return self._process_transfer_events(data.get('result', []), network)
791
+
792
+ except Exception as e:
793
+ print(f"⚠️ فشل جلب تحركات من {network}: {e}")
794
+
795
+ return []
796
+
797
+ def _process_transfer_events(self, events, network):
798
+ """معالجة أحداث التحويل لاكتشاف التحركات الكبيرة"""
799
+ large_transfers = []
800
+
801
+ for event in events[:10]: # تحليل أول 10 أحداث فقط للأداء
802
+ try:
803
+ # استخراج كمية التحويل (هذا مثال مبسط)
804
+ # في التطبيق الحقيقي تحتاج لتحليل أكثر تعقيداً
805
+ if 'data' in event and len(event['data']) >= 66:
806
+ # تحويل hex إلى قيمة رقمية
807
+ value_hex = event['data'][2:66] # إزالة 0x
808
+ value = int(value_hex, 16)
809
+
810
+ # اعتبار التحويلات الكبيرة كتحركات حيتان
811
+ if value > 1000000000000000000: # 1 ETH أو ما يعادله
812
+ large_transfers.append({
813
+ 'network': network,
814
+ 'value': value,
815
+ 'transaction_hash': event.get('transactionHash', ''),
816
+ 'from': event.get('topics', [])[1] if len(event.get('topics', [])) > 1 else '',
817
+ 'to': event.get('topics', [])[2] if len(event.get('topics', [])) > 2 else '',
818
+ 'timestamp': datetime.now().isoformat()
819
+ })
820
+ except Exception:
821
+ continue
822
+
823
+ return large_transfers
824
+
825
+ # إنشاء نسخة عالمية
826
+ whale_monitor_global = ReliableWhaleMonitor()
827
+
828
+ class DataManager:
829
+ def __init__(self, contracts_db):
830
+ self.contracts_db = contracts_db or {}
831
+ self.exchange = ccxt.kucoin()
832
+ self.exchange.rateLimit = 1000
833
+ self._http_client = None
834
+ self.fetch_stats = {'successful_fetches': 0, 'failed_fetches': 0, 'rate_limit_hits': 0}
835
+
836
+ # ✅ نظامي مراقبة الحيتان
837
+ self.general_whale_monitor = ReliableWhaleMonitor(contracts_db)
838
+ self.specific_whale_monitor = SpecificWhaleMonitor(contracts_db, self.general_whale_monitor)
839
+
840
+ async def initialize(self):
841
+ print("✅ DataManager initialized - DUAL WHALE MONITORING SYSTEM WITH RPC SUPPORT")
842
+
843
+ async def close(self):
844
+ """إغلاق جميع العملاء بشكل آمن"""
845
+ if self._http_client:
846
+ await self._http_client.aclose()
847
+ self._http_client = None
848
+ await self.general_whale_monitor.close()
849
+ if hasattr(self.exchange, 'close'):
850
+ await self.exchange.close()
851
+
852
+ async def get_whale_data_safe_async(self, symbol):
853
+ """دالة آمنة لجلب بيانات الحيتان - الإصلاح"""
854
+ try:
855
+ # استخدام البيانات العامة للحيتان كبديل
856
+ general_analysis = await self.general_whale_monitor.get_comprehensive_whale_analysis()
857
+
858
+ return {
859
+ 'data_available': general_analysis.get('data_available', False),
860
+ 'transfer_count': general_analysis.get('total_transactions', 0),
861
+ 'total_volume': general_analysis.get('total_volume_usd', 0),
862
+ 'source': 'general_whale_monitor',
863
+ 'symbol': symbol
864
+ }
865
+ except Exception as e:
866
+ print(f"⚠️ فشل جلب بيانات الحيتان لـ {symbol}: {e}")
867
+ return {
868
+ 'data_available': False,
869
+ 'transfer_count': 0,
870
+ 'total_volume': 0,
871
+ 'source': 'fallback',
872
+ 'symbol': symbol
873
+ }
874
+
875
+ async def get_enhanced_market_context(self):
876
+ """جلب سياق السوق مع بيانات الحيتان العامة والخاصة"""
877
+ try:
878
+ print("📊 جلب سياق السوق المحسن...")
879
+
880
+ # ✅ جلب البيانات الأساسية
881
+ sentiment_task = self.get_sentiment_safe_async()
882
+ price_task = self._get_prices_safe_async()
883
+ general_whale_task = self.general_whale_monitor.get_comprehensive_whale_analysis()
884
+
885
+ results = await asyncio.gather(
886
+ sentiment_task, price_task, general_whale_task,
887
+ return_exceptions=True
888
+ )
889
+
890
+ sentiment_data = results[0] if not isinstance(results[0], Exception) else None
891
+ price_data = results[1] if not isinstance(results[1], Exception) else {}
892
+ general_whale_impact = results[2] if not isinstance(results[2], Exception) else None
893
+
894
+ bitcoin_price = price_data.get('bitcoin')
895
+ ethereum_price = price_data.get('ethereum')
896
+
897
+ # ✅ استخدام بيانات KuCoin كبديل إذا فشل CoinGecko
898
+ if not bitcoin_price or not ethereum_price:
899
+ print("🔄 استخدام بيانات KuCoin كبديل للأسعار...")
900
+ try:
901
+ btc_ticker = await self.exchange.fetch_ticker('BTC/USDT')
902
+ eth_ticker = await self.exchange.fetch_ticker('ETH/USDT')
903
+ bitcoin_price = btc_ticker.get('last')
904
+ ethereum_price = eth_ticker.get('last')
905
+ print(f"✅ أسعار KuCoin البديلة: BTC=${bitcoin_price}, ETH=${ethereum_price}")
906
+ except Exception as e:
907
+ print(f"❌ فشل جلب الأسعار البديلة: {e}")
908
+
909
+ if not bitcoin_price or not ethereum_price:
910
+ print("❌ لا توجد بيانات أسعار حقيقية")
911
+ return None
912
+
913
+ # ✅ تحليل اتجاه السوق
914
+ market_trend = self._determine_market_trend(bitcoin_price, sentiment_data, general_whale_impact)
915
+
916
+ # ✅ استخدام البيانات الاحتياطية إذا لزم الأمر
917
+ if not general_whale_impact:
918
+ print("⚠️ استخدام بيانات حيتان احتياطية")
919
+ general_whale_impact = self.general_whale_monitor._get_fallback_analysis()
920
+
921
+ market_context = {
922
+ 'timestamp': datetime.now().isoformat(),
923
+ 'bitcoin_price_usd': bitcoin_price,
924
+ 'ethereum_price_usd': ethereum_price,
925
+ 'fear_and_greed_index': sentiment_data.get('feargreed_value') if sentiment_data else None,
926
+ 'sentiment_class': sentiment_data.get('feargreed_class') if sentiment_data else None,
927
+ 'general_whale_activity': await self._get_legacy_whale_data(general_whale_impact),
928
+ 'whale_impact_analysis': general_whale_impact,
929
+ 'market_trend': market_trend,
930
+ 'btc_sentiment': 'BULLISH' if bitcoin_price and bitcoin_price > 60000 else 'BEARISH' if bitcoin_price and bitcoin_price < 55000 else 'NEUTRAL',
931
+ 'data_sources': {
932
+ 'prices': bool(bitcoin_price),
933
+ 'sentiment': bool(sentiment_data and sentiment_data.get('feargreed_value')),
934
+ 'general_whale_data': general_whale_impact.get('data_available', False)
935
+ },
936
+ 'data_quality': 'HIGH' if bitcoin_price and sentiment_data and general_whale_impact else 'LOW'
937
+ }
938
+
939
+ print(f"📊 سياق السوق المحسن: BTC=${bitcoin_price:,.0f}, F&G={market_context['fear_and_greed_index']}")
940
+
941
+ return market_context
942
+
943
+ except Exception as e:
944
+ print(f"❌ فشل جلب سياق السوق المحسن: {e}")
945
+ return None
946
+
947
+ async def get_candidate_specific_whale_analysis(self, candidates):
948
+ """جلب تحليل الحيتان الخاص للمرشحين"""
949
+ if not candidates:
950
+ return {}
951
+
952
+ symbols = [candidate.get('symbol') for candidate in candidates if candidate.get('symbol')]
953
+ return await self.specific_whale_monitor.get_specific_whale_activity(symbols)
954
+
955
+ async def get_sentiment_safe_async(self):
956
+ """جلب بيانات المشاعر الحقيقية فقط"""
957
+ max_retries = 3
958
+ for attempt in range(max_retries):
959
+ try:
960
+ print(f"🎭 جلب بيانات المشاعر الحقيقية - المحاولة {attempt + 1}/{max_retries}...")
961
+
962
+ async with httpx.AsyncClient(timeout=15.0) as client:
963
+ response = await client.get("https://api.alternative.me/fng/")
964
+ response.raise_for_status()
965
+ data = response.json()
966
+
967
+ if 'data' not in data or not data['data']:
968
+ raise ValueError("بيانات المشاعر غير متوفرة في الاستجابة")
969
+
970
+ latest_data = data['data'][0]
971
+ print(f"✅ تم جلب بيانات المشاعر الحقيقية: {latest_data['value']} ({latest_data['value_classification']})")
972
+
973
+ return {
974
+ "feargreed_value": int(latest_data['value']),
975
+ "feargreed_class": latest_data['value_classification'],
976
+ "source": "alternative.me",
977
+ "timestamp": datetime.now().isoformat()
978
+ }
979
+
980
+ except Exception as e:
981
+ print(f"⚠️ فشل محاولة {attempt + 1}/{max_retries} لجلب بيانات المشاعر: {e}")
982
+ if attempt < max_retries - 1:
983
+ await asyncio.sleep(2)
984
+
985
+ print("🚨 فشل جميع محاولات جلب بيانات المشاعر الحقيقية")
986
+ return None
987
+
988
+ async def get_market_context_async(self):
989
+ """جلب سياق السوق مع البيانات الحقيقية فقط"""
990
+ return await self.get_enhanced_market_context()
991
+
992
+ async def _get_prices_safe_async(self):
993
+ """جلب الأسعار الحقيقية فقط"""
994
+ try:
995
+ async with httpx.AsyncClient(timeout=15.0) as client:
996
+ response = await client.get('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd')
997
+ if response.status_code == 200:
998
+ data = response.json()
999
+ bitcoin_price = data.get('bitcoin', {}).get('usd')
1000
+ ethereum_price = data.get('ethereum', {}).get('usd')
1001
+
1002
+ if bitcoin_price and ethereum_price:
1003
+ return {
1004
+ 'bitcoin': bitcoin_price,
1005
+ 'ethereum': ethereum_price
1006
+ }
1007
+ elif response.status_code == 429:
1008
+ print("⚠️ CoinGecko rate limit reached, using exchange prices")
1009
+ except Exception as e:
1010
+ print(f"⚠️ فشل جلب الأسعار الحقيقية: {e}")
1011
+
1012
+ return {}
1013
+
1014
+ async def _get_legacy_whale_data(self, whale_impact):
1015
+ """تحويل بيانات التأثير إلى تنسيق legacy"""
1016
+ if not whale_impact:
1017
+ return {
1018
+ 'data_available': False,
1019
+ 'description': 'No real data available',
1020
+ 'critical_alert': False,
1021
+ 'sentiment': 'NEUTRAL',
1022
+ 'total_volume_usd': 0,
1023
+ 'transaction_count': 0
1024
+ }
1025
+
1026
+ sentiment_map = {
1027
+ 'BULLISH': 'BULLISH',
1028
+ 'SLIGHTLY_BULLISH': 'BULLISH',
1029
+ 'NEUTRAL': 'NEUTRAL',
1030
+ 'SLIGHTLY_BEARISH': 'BEARISH',
1031
+ 'BEARISH': 'BEARISH'
1032
+ }
1033
+
1034
+ sentiment = sentiment_map.get(
1035
+ whale_impact.get('trading_intents', {}).get('market_sentiment', 'NEUTRAL'),
1036
+ 'NEUTRAL'
1037
+ )
1038
+
1039
+ critical_alert = whale_impact['overall_impact'] == 'HIGH'
1040
+
1041
+ return {
1042
+ 'data_available': whale_impact['data_available'],
1043
+ 'description': whale_impact['description'],
1044
+ 'critical_alert': critical_alert,
1045
+ 'sentiment': sentiment,
1046
+ 'total_volume_usd': whale_impact['total_volume_usd'],
1047
+ 'transaction_count': whale_impact['total_transactions']
1048
+ }
1049
+
1050
+ def _determine_market_trend(self, bitcoin_price, sentiment_data, whale_impact):
1051
+ """تحديد اتجاه السوق بناءً على البيانات الحقيقية"""
1052
+ try:
1053
+ if not bitcoin_price:
1054
+ return "unknown"
1055
+
1056
+ score = 0
1057
+
1058
+ # تحليل سعر البيتكوين الحقيقي
1059
+ if bitcoin_price > 60000:
1060
+ score += 1
1061
+ elif bitcoin_price < 55000:
1062
+ score -= 1
1063
+
1064
+ # تحليل مؤشر الخوف والجشع الحقيقي
1065
+ if sentiment_data:
1066
+ fear_greed = sentiment_data.get('feargreed_value')
1067
+ if fear_greed and fear_greed > 60:
1068
+ score += 1
1069
+ elif fear_greed and fear_greed < 40:
1070
+ score -= 1
1071
+
1072
+ # تحليل تأثير الحيتان الحقيقي
1073
+ if whale_impact:
1074
+ if whale_impact.get('overall_impact') == 'HIGH':
1075
+ score -= 1
1076
+ elif whale_impact.get('overall_impact') == 'MEDIUM':
1077
+ score += 0
1078
+
1079
+ # ✅ تحليل نوايا الحيتان
1080
+ trading_intents = whale_impact.get('trading_intents', {})
1081
+ market_sentiment = trading_intents.get('market_sentiment', 'NEUTRAL')
1082
+
1083
+ if market_sentiment in ['BULLISH', 'SLIGHTLY_BULLISH']:
1084
+ score += 1
1085
+ elif market_sentiment in ['BEARISH', 'SLIGHTLY_BEARISH']:
1086
+ score -= 1
1087
+
1088
+ if score >= 2:
1089
+ return "bull_market"
1090
+ elif score <= -2:
1091
+ return "bear_market"
1092
+ elif -1 <= score <= 1:
1093
+ return "sideways_market"
1094
+ else:
1095
+ return "volatile_market"
1096
+
1097
+ except Exception as e:
1098
+ print(f"⚠️ خطأ في تحديد اتجاه السوق: {e}")
1099
+ return "unknown"
1100
+
1101
+ # ✅ باقي دوال DataManager الأساسية
1102
+ @staticmethod
1103
+ def adaptive_backoff(func):
1104
+ @backoff.on_exception(backoff.expo, (RateLimitExceeded, DDoSProtection, NetworkError, httpx.TimeoutException), max_tries=3, max_time=20)
1105
+ @wraps(func)
1106
+ async def wrapper(*args, **kwargs):
1107
+ return await func(*args, **kwargs)
1108
+ return wrapper
1109
+
1110
+ @adaptive_backoff
1111
+ async def fetch_ohlcv_with_retry(self, symbol: str, timeframe: str, limit: int = 100):
1112
+ """جلب بيانات OHLCV الحقيقية مع إعادة المحاولة"""
1113
+ try:
1114
+ candles = await self.exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=limit)
1115
+ self.fetch_stats['successful_fetches'] += 1
1116
+ return candles
1117
+ except RateLimitExceeded:
1118
+ self.fetch_stats['rate_limit_hits'] += 1
1119
+ print(f"⚠️ Rate limit hit for {symbol} on {timeframe}")
1120
+ raise
1121
+ except Exception as e:
1122
+ self.fetch_stats['failed_fetches'] += 1
1123
+ print(f"❌ Failed to fetch {symbol} {timeframe}: {e}")
1124
+ raise
1125
+
1126
+ async def find_high_potential_candidates(self, n=100):
1127
+ """إيجاد مرشحين عاليي الإمكانية مع بيانات الحيتان الخاصة"""
1128
+ print("🚀 Starting High Potential Candidate Scan with Specific Whale Data...")
1129
+
1130
+ try:
1131
+ all_tickers = list((await self.exchange.fetch_tickers()).values())
1132
+ if not all_tickers:
1133
+ print("❌ Failed to fetch real tickers data")
1134
+ return []
1135
+
1136
+ usdt_tickers = [
1137
+ t for t in all_tickers
1138
+ if '/USDT' in t.get('symbol', '')
1139
+ and t.get('quoteVolume', 0) > 100000
1140
+ ]
1141
+
1142
+ if not usdt_tickers:
1143
+ print("❌ No USDT pairs with sufficient real volume")
1144
+ return []
1145
+
1146
+ top_volume_symbols = [
1147
+ t['symbol'] for t in sorted(usdt_tickers, key=lambda x: x.get('quoteVolume', 0), reverse=True)[:200]
1148
+ ]
1149
+
1150
+ print(f"📊 Analyzing {len(top_volume_symbols)} symbols with highest real volume")
1151
+
1152
+ # المسوحات الأساسية ببيانات حقيقية
1153
+ momentum_candidates = await self._scan_for_momentum(usdt_tickers, top_n=30)
1154
+ breakout_candidates = await self._scan_for_breakouts(top_volume_symbols, top_n=30)
1155
+ volume_spike_candidates = await self._scan_for_volume_spike(top_volume_symbols, top_n=30)
1156
+
1157
+ combined_candidates = {}
1158
+ for candidates_dict in [momentum_candidates, breakout_candidates, volume_spike_candidates]:
1159
+ for symbol, reasons in candidates_dict.items():
1160
+ combined_candidates.setdefault(symbol, set()).update(reasons)
1161
+
1162
+ if not MARKET_STATE_OK:
1163
+ print("🚨 Risk filter: Market state is NOT OK. Halting search.")
1164
+ return []
1165
+
1166
+ final_list = []
1167
+ tickers_map = {t['symbol']: t for t in usdt_tickers}
1168
+
1169
+ for symbol, reasons in combined_candidates.items():
1170
+ ticker_info = tickers_map.get(symbol)
1171
+ if not ticker_info:
1172
+ continue
1173
+
1174
+ change_percent = ticker_info.get('change', 0) or 0
1175
+ if change_percent > 500:
1176
+ continue
1177
+
1178
+ quote_volume = ticker_info.get('quoteVolume', 0)
1179
+ if quote_volume < 50000:
1180
+ continue
1181
+
1182
+ final_list.append({
1183
+ 'symbol': symbol,
1184
+ 'reasons': list(reasons),
1185
+ 'score': len(reasons),
1186
+ 'change_percent': change_percent,
1187
+ 'volume': quote_volume
1188
+ })
1189
+
1190
+ sorted_final_list = sorted(final_list, key=lambda x: x['score'], reverse=True)
1191
+
1192
+ # ✅ إضافة بيانات الحيتان الخاصة للمرشحين
1193
+ if sorted_final_list:
1194
+ specific_whale_data = await self.get_candidate_specific_whale_analysis(sorted_final_list)
1195
+
1196
+ for candidate in sorted_final_list:
1197
+ symbol = candidate.get('symbol')
1198
+ if symbol in specific_whale_data:
1199
+ candidate['specific_whale_activity'] = specific_whale_data[symbol]
1200
+ # تحسين النقاط بناءً على نشاط الحيتان الخاص
1201
+ whale_score = self._calculate_whale_score(specific_whale_data[symbol])
1202
+ candidate['whale_score'] = whale_score
1203
+ candidate['final_score'] = candidate.get('final_score', 0.5) + (whale_score * 0.2)
1204
+ print(f"🐋 Added specific whale data for {symbol} - Score: {whale_score:.2f}")
1205
+
1206
+ print(f"✅ Real data scan complete. Found {len(sorted_final_list)} potential candidates with whale data.")
1207
+
1208
+ return sorted_final_list[:n]
1209
+
1210
+ except Exception as e:
1211
+ print(f"❌ Failed to find high potential candidates with real data: {e}")
1212
+ traceback.print_exc()
1213
+ return []
1214
+
1215
+ def _calculate_whale_score(self, whale_data):
1216
+ """حساب نقاط الحيتان بناءً على النشاط"""
1217
+ if not whale_data:
1218
+ return 0.0
1219
+
1220
+ score = 0.0
1221
+
1222
+ # نقاط بناءً على حجم التداول
1223
+ total_volume = whale_data.get('total_volume_24h', 0)
1224
+ if total_volume > 1000000: # 1M+
1225
+ score += 3.0
1226
+ elif total_volume > 500000: # 500K+
1227
+ score += 2.0
1228
+ elif total_volume > 100000: # 100K+
1229
+ score += 1.0
1230
+
1231
+ # نقاط بناءً على عدد المعاملات
1232
+ transaction_count = whale_data.get('whale_transaction_count', 0)
1233
+ if transaction_count > 10:
1234
+ score += 2.0
1235
+ elif transaction_count > 5:
1236
+ score += 1.0
1237
+
1238
+ # نقاط بناءً على التحركات الكبيرة على الشبكة
1239
+ large_tx_count = len(whale_data.get('large_transactions', []))
1240
+ if large_tx_count > 5:
1241
+ score += 2.0
1242
+ elif large_tx_count > 2:
1243
+ score += 1.0
1244
+
1245
+ return min(score / 7.0, 1.0) # تطبيع إلى 0-1
1246
+
1247
+ async def _scan_for_momentum(self, tickers, top_n=30):
1248
+ """مسح الزخم ببيانات حقيقية"""
1249
+ print("🔍 Running Real Momentum Scanner...")
1250
+ valid_tickers = [t for t in tickers if t.get('change') is not None]
1251
+ if not valid_tickers:
1252
+ print("⚠️ No valid tickers for real momentum analysis")
1253
+ return {}
1254
+
1255
+ sorted_by_change = sorted(valid_tickers, key=lambda x: x.get('change', 0), reverse=True)
1256
+ return {ticker['symbol']: {'momentum'} for ticker in sorted_by_change[:top_n]}
1257
+
1258
+ async def _scan_for_breakouts(self, symbols, top_n=30):
1259
+ """مسح الانكسارات ببيانات حقيقية"""
1260
+ print("🧱 Running Real Breakout Scanner...")
1261
+ candidates = {}
1262
+ tasks = [self.exchange.fetch_ohlcv(symbol, '1h', limit=24) for symbol in symbols]
1263
+ results = await asyncio.gather(*tasks, return_exceptions=True)
1264
+
1265
+ for i, result in enumerate(results):
1266
+ if isinstance(result, Exception) or not result:
1267
+ continue
1268
+
1269
+ df = pd.DataFrame(result, columns=['time', 'open', 'high', 'low', 'close', 'volume'])
1270
+ if len(df) < 2:
1271
+ continue
1272
+
1273
+ recent_high = df['high'].iloc[:-1].max()
1274
+ current_price = df['close'].iloc[-1]
1275
+
1276
+ if current_price > recent_high * 1.02:
1277
+ candidates[symbols[i]] = {'breakout'}
1278
+ if len(candidates) >= top_n:
1279
+ break
1280
+
1281
+ print(f"✅ Found {len(candidates)} real breakout candidates")
1282
+ return candidates
1283
+
1284
+ async def _scan_for_volume_spike(self, symbols, top_n=30):
1285
+ """مسح ارتفاع الحجم ببيانات حقيقية"""
1286
+ print("💧 Running Real Volume Spike Scanner...")
1287
+ candidates = {}
1288
+ tasks = [self.exchange.fetch_ohlcv(symbol, '1h', limit=24) for symbol in symbols]
1289
+ results = await asyncio.gather(*tasks, return_exceptions=True)
1290
+
1291
+ for i, result in enumerate(results):
1292
+ if isinstance(result, Exception) or not result:
1293
+ continue
1294
+
1295
+ df = pd.DataFrame(result, columns=['time', 'open', 'high', 'low', 'close', 'volume'])
1296
+ df['volume'] = pd.to_numeric(df['volume'])
1297
+ if len(df) < 2:
1298
+ continue
1299
+
1300
+ average_volume = df['volume'].iloc[:-1].mean()
1301
+ current_volume = df['volume'].iloc[-1]
1302
+
1303
+ if current_volume > average_volume * 3 and current_volume > 10000:
1304
+ candidates[symbols[i]] = {'volume_spike'}
1305
+ if len(candidates) >= top_n:
1306
+ break
1307
+
1308
+ print(f"✅ Found {len(candidates)} real volume spike candidates")
1309
+ return candidates
1310
+
1311
+ async def get_fast_pass_data_async(self, symbols_with_reasons):
1312
+ """جلب بيانات سريعة حقيقية للمرشحين"""
1313
+ timeframes = ['5m', '15m', '1h', '4h', '1d', '1w']
1314
+ data = []
1315
+ total = len(symbols_with_reasons)
1316
+ completed = 0
1317
+ success = 0
1318
+ failed = 0
1319
+ semaphore = asyncio.Semaphore(5)
1320
+
1321
+ async def fetch_symbol_data(symbol_data, index):
1322
+ nonlocal completed, success, failed
1323
+ symbol = symbol_data['symbol']
1324
+ reasons = symbol_data['reasons']
1325
+
1326
+ async with semaphore:
1327
+ try:
1328
+ print(f"⏳ [{index+1}/{total}] Fetching real data for {symbol}...")
1329
+ ohlcv_data = {}
1330
+ timeframes_fetched = 0
1331
+
1332
+ for timeframe in timeframes:
1333
+ try:
1334
+ candles = await self.fetch_ohlcv_with_retry(symbol, timeframe, limit=100)
1335
+
1336
+ if candles and len(candles) >= 20:
1337
+ cleaned_candles = []
1338
+ for candle in candles:
1339
+ if len(candle) >= 6:
1340
+ cleaned_candles.append([
1341
+ candle[0],
1342
+ float(candle[1]),
1343
+ float(candle[2]),
1344
+ float(candle[3]),
1345
+ float(candle[4]),
1346
+ float(candle[5])
1347
+ ])
1348
+
1349
+ if len(cleaned_candles) >= 20:
1350
+ ohlcv_data[timeframe] = cleaned_candles
1351
+ timeframes_fetched += 1
1352
+ else:
1353
+ ohlcv_data[timeframe] = []
1354
+ else:
1355
+ ohlcv_data[timeframe] = []
1356
+
1357
+ except Exception as e:
1358
+ ohlcv_data[timeframe] = []
1359
+
1360
+ await asyncio.sleep(0.2)
1361
+
1362
+ completed += 1
1363
+
1364
+ if timeframes_fetched >= 2:
1365
+ success += 1
1366
+ print(f"✅ [{index+1}/{total}] {symbol} - {timeframes_fetched}/{len(timeframes)} real timeframes")
1367
+ return {
1368
+ 'symbol': symbol,
1369
+ 'ohlcv': ohlcv_data,
1370
+ 'reasons': reasons,
1371
+ 'successful_timeframes': timeframes_fetched
1372
+ }
1373
+ else:
1374
+ failed += 1
1375
+ print(f"⚠️ [{index+1}/{total}] {symbol} - Insufficient real data")
1376
+ return None
1377
+
1378
+ except Exception as e:
1379
+ completed += 1
1380
+ failed += 1
1381
+ print(f"❌ [{index+1}/{total}] {symbol} - Real data error")
1382
+ return None
1383
+
1384
+ print(f"📊 Starting real data fetch for {total} symbols")
1385
+
1386
+ tasks = [fetch_symbol_data(symbol_data, i) for i, symbol_data in enumerate(symbols_with_reasons)]
1387
+ results = await asyncio.gather(*tasks, return_exceptions=True)
1388
+
1389
+ for result in results:
1390
+ if isinstance(result, Exception):
1391
+ continue
1392
+ elif result is not None:
1393
+ data.append(result)
1394
+
1395
+ success_rate = (success/total*100) if total > 0 else 0
1396
+ print(f"✅ Real data fetching complete! Success: {success}/{total} ({success_rate:.1f}%)")
1397
+
1398
+ return data
1399
+
1400
+ async def get_latest_price_async(self, symbol):
1401
+ """جلب آخر سعر حقيقي لرمز"""
1402
+ max_retries = 2
1403
+ for attempt in range(max_retries):
1404
+ try:
1405
+ ticker = await self.exchange.fetch_ticker(symbol)
1406
+ price = ticker.get('last')
1407
+ if price and price > 0:
1408
+ return price
1409
+ except Exception as e:
1410
+ print(f"⚠️ فشل محاولة {attempt + 1}/{max_retries} لجلب سعر حقيقي لـ {symbol}: {e}")
1411
+ if attempt < max_retries - 1:
1412
+ await asyncio.sleep(1)
1413
+
1414
+ print(f"❌ فشل جميع محاولات جلب سعر حقيقي لـ {symbol}")
1415
+ return None
1416
+
1417
+ def get_performance_stats(self):
1418
+ """الحصول على إحصائيات الأداء الحقيقية"""
1419
+ total_attempts = self.fetch_stats['successful_fetches'] + self.fetch_stats['failed_fetches']
1420
+ success_rate = (self.fetch_stats['successful_fetches'] / total_attempts * 100) if total_attempts > 0 else 0
1421
+
1422
+ return {
1423
+ 'total_attempts': total_attempts,
1424
+ 'successful_fetches': self.fetch_stats['successful_fetches'],
1425
+ 'failed_fetches': self.fetch_stats['failed_fetches'],
1426
+ 'rate_limit_hits': self.fetch_stats['rate_limit_hits'],
1427
+ 'success_rate': f"{success_rate:.1f}%",
1428
+ 'timestamp': datetime.now().isoformat()
1429
+ }
1430
+
1431
+ print("✅ ENHANCED Data Manager Loaded - DUAL WHALE MONITORING SYSTEM - GENERAL & SPECIFIC ANALYSIS - MULTI-NETWORK RPC SUPPORT")
learning_engine (19).py ADDED
@@ -0,0 +1,644 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import asyncio
4
+ import pandas as pd
5
+ import numpy as np
6
+ from datetime import datetime, timedelta
7
+ from typing import Dict, List, Any
8
+ import hashlib
9
+
10
+ class LearningEngine:
11
+ def __init__(self, r2_service, data_manager):
12
+ self.r2_service = r2_service
13
+ self.data_manager = data_manager
14
+ self.weights = {}
15
+ self.performance_history = []
16
+ self.strategy_effectiveness = {}
17
+ self.market_patterns = {}
18
+ self.risk_profiles = {}
19
+ self.initialized = False
20
+ self.initialization_lock = asyncio.Lock()
21
+
22
+ async def initialize(self):
23
+ """تهيئة نظام التعلم من R2"""
24
+ async with self.initialization_lock:
25
+ if self.initialized:
26
+ return
27
+
28
+ print("🧠 تهيئة نظام التعلم الذاتي...")
29
+ try:
30
+ await self.load_weights_from_r2()
31
+ await self.load_performance_history()
32
+ self.initialized = True
33
+ print("✅ نظام التعلم جاهز - الأوزان محملة بنجاح")
34
+ except Exception as e:
35
+ print(f"⚠️ لم يتم تحميل الأوزان السابقة: {e} - سيتم البدء بأوزان افتراضية")
36
+ await self.initialize_default_weights()
37
+ self.initialized = True
38
+
39
+ async def initialize_enhanced(self):
40
+ """تهيئة محسنة لنظام التعلم"""
41
+ async with self.initialization_lock:
42
+ if self.initialized:
43
+ return
44
+
45
+ print("🧠 تهيئة نظام التعلم الذاتي المحسّن...")
46
+ try:
47
+ await self.load_weights_from_r2()
48
+ await self.load_performance_history()
49
+
50
+ # إصلاح هيكل الأوزان إذا لزم الأمر
51
+ await self.fix_weights_structure()
52
+
53
+ # إذا لم تكن هناك بيانات كافية، بدء التعلم من الصفر
54
+ if not self.performance_history:
55
+ print("🔰 بدء التعلم من الصفر - لا توجد بيانات تاريخية")
56
+ await self.initialize_default_weights()
57
+
58
+ self.initialized = True
59
+ print("✅ نظام التعلم المحسّن جاهز")
60
+
61
+ except Exception as e:
62
+ print(f"⚠️ فشل التهيئة المحسنة: {e}")
63
+ await self.initialize_default_weights()
64
+ self.initialized = True
65
+
66
+ async def fix_weights_structure(self):
67
+ """إصلاح هيكل الأوزان ليتوافق مع الكود"""
68
+ try:
69
+ # تحميل البيانات الحالية
70
+ key = "learning_engine_weights.json"
71
+ response = self.r2_service.s3_client.get_object(Bucket="trading", Key=key)
72
+ current_data = json.loads(response['Body'].read())
73
+
74
+ # إذا كان الهيكل قديماً، قم بتحديثه
75
+ if 'strategy_weights' in current_data and 'last_updated' not in current_data:
76
+ fixed_data = {
77
+ "weights": current_data,
78
+ "last_updated": datetime.now().isoformat(),
79
+ "version": "2.0",
80
+ "performance_metrics": await self.calculate_performance_metrics()
81
+ }
82
+
83
+ data_json = json.dumps(fixed_data, indent=2, ensure_ascii=False).encode('utf-8')
84
+ self.r2_service.s3_client.put_object(
85
+ Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
86
+ )
87
+ print("✅ تم إصلاح هيكل الأوزان بنجاح")
88
+
89
+ except Exception as e:
90
+ print(f"⚠️ لم يتم إصلاح هيكل الأوزان: {e}")
91
+
92
+ async def initialize_default_weights(self):
93
+ """تهيئة الأوزان الافتراضية - موزعة بشكل أفضل"""
94
+ self.weights = {
95
+ "strategy_weights": {
96
+ "trend_following": 0.18,
97
+ "mean_reversion": 0.15,
98
+ "breakout_momentum": 0.22,
99
+ "volume_spike": 0.12,
100
+ "whale_tracking": 0.15,
101
+ "pattern_recognition": 0.10,
102
+ "hybrid_ai": 0.08
103
+ },
104
+ "technical_weights": {
105
+ "rsi": 0.15,
106
+ "macd": 0.18,
107
+ "ema_cross": 0.12,
108
+ "bollinger_bands": 0.10,
109
+ "volume_analysis": 0.15,
110
+ "support_resistance": 0.12,
111
+ "market_sentiment": 0.18
112
+ },
113
+ "risk_parameters": {
114
+ "max_position_size": 0.1,
115
+ "max_daily_loss": 0.02,
116
+ "stop_loss_base": 0.02,
117
+ "risk_reward_ratio": 2.0,
118
+ "volatility_adjustment": 1.0
119
+ },
120
+ "market_condition_weights": {
121
+ "bull_market": {
122
+ "trend_following": 0.25,
123
+ "breakout_momentum": 0.20,
124
+ "whale_tracking": 0.15
125
+ },
126
+ "bear_market": {
127
+ "mean_reversion": 0.25,
128
+ "pattern_recognition": 0.20,
129
+ "hybrid_ai": 0.15
130
+ },
131
+ "sideways_market": {
132
+ "mean_reversion": 0.30,
133
+ "volume_spike": 0.20,
134
+ "pattern_recognition": 0.15
135
+ }
136
+ }
137
+ }
138
+
139
+ async def load_weights_from_r2(self):
140
+ """تحميل الأوزان من R2"""
141
+ try:
142
+ key = "learning_engine_weights.json"
143
+ response = self.r2_service.s3_client.get_object(
144
+ Bucket="trading", Key=key
145
+ )
146
+ weights_data = json.loads(response['Body'].read())
147
+
148
+ # التعامل مع الهيكل الجديد والقديم
149
+ if isinstance(weights_data, dict):
150
+ if 'weights' in weights_data:
151
+ self.weights = weights_data['weights']
152
+ else:
153
+ self.weights = weights_data
154
+ print(f"✅ تم تحميل الأوزان من R2 بنجاح. إصدار الهيكل: {'جديد' if 'weights' in weights_data else 'قديم'}")
155
+ else:
156
+ raise ValueError("هيكل الأوزان غير صحيح")
157
+
158
+ except Exception as e:
159
+ print(f"❌ فشل تحميل الأوزان: {e}")
160
+ await self.initialize_default_weights()
161
+ await self.save_weights_to_r2()
162
+
163
+ async def save_weights_to_r2(self):
164
+ """حفظ الأوزان إلى R2"""
165
+ try:
166
+ key = "learning_engine_weights.json"
167
+ weights_data = {
168
+ "weights": self.weights,
169
+ "last_updated": datetime.now().isoformat(),
170
+ "version": "2.0",
171
+ "performance_metrics": await self.calculate_performance_metrics()
172
+ }
173
+
174
+ data_json = json.dumps(weights_data, indent=2, ensure_ascii=False).encode('utf-8')
175
+ self.r2_service.s3_client.put_object(
176
+ Bucket="trading",
177
+ Key=key,
178
+ Body=data_json,
179
+ ContentType="application/json"
180
+ )
181
+ print("✅ تم حفظ الأوزان إلى R2 بنجاح")
182
+ except Exception as e:
183
+ print(f"❌ فشل حفظ الأوزان: {e}")
184
+
185
+ async def load_performance_history(self):
186
+ """تحميل سجل الأداء"""
187
+ try:
188
+ key = "learning_performance_history.json"
189
+ response = self.r2_service.s3_client.get_object(
190
+ Bucket="trading", Key=key
191
+ )
192
+ history_data = json.loads(response['Body'].read())
193
+ self.performance_history = history_data.get("history", [])
194
+ print(f"✅ تم تحميل سجل الأداء - {len(self.performance_history)} تسجيل")
195
+ except Exception as e:
196
+ print(f"⚠️ لم يتم تحميل سجل الأداء: {e}")
197
+ self.performance_history = []
198
+
199
+ async def save_performance_history(self):
200
+ """حفظ سجل الأداء"""
201
+ try:
202
+ key = "learning_performance_history.json"
203
+ history_data = {
204
+ "history": self.performance_history[-1000:],
205
+ "last_updated": datetime.now().isoformat()
206
+ }
207
+
208
+ data_json = json.dumps(history_data, indent=2, ensure_ascii=False).encode('utf-8')
209
+ self.r2_service.s3_client.put_object(
210
+ Bucket="trading",
211
+ Key=key,
212
+ Body=data_json,
213
+ ContentType="application/json"
214
+ )
215
+ except Exception as e:
216
+ print(f"❌ فشل حفظ سجل الأداء: {e}")
217
+
218
+ async def analyze_trade_outcome(self, trade_data, outcome):
219
+ """تحليل نتيجة الصفقة وتحديث الأوزان - الإصدار المحسّن"""
220
+ if not self.initialized:
221
+ await self.initialize()
222
+
223
+ try:
224
+ # استخراج الاستراتيجية من بيانات الصفقة
225
+ strategy = trade_data.get('strategy', 'unknown')
226
+ if strategy == 'unknown':
227
+ decision_data = trade_data.get('decision_data', {})
228
+ strategy = decision_data.get('strategy', 'unknown')
229
+
230
+ # الحصول على سياق السوق الحقيقي
231
+ market_context = await self.get_current_market_conditions()
232
+
233
+ analysis_entry = {
234
+ "timestamp": datetime.now().isoformat(),
235
+ "trade_data": trade_data,
236
+ "outcome": outcome,
237
+ "market_conditions": market_context,
238
+ "strategy_used": strategy,
239
+ "symbol": trade_data.get('symbol', 'unknown'),
240
+ "pnl_usd": trade_data.get('pnl_usd', 0),
241
+ "pnl_percent": trade_data.get('pnl_percent', 0)
242
+ }
243
+
244
+ self.performance_history.append(analysis_entry)
245
+
246
+ await self.update_strategy_effectiveness(analysis_entry)
247
+ await self.update_market_patterns(analysis_entry)
248
+
249
+ # ✅ التحديث الهام: تحديث الأوزان بعد كل صفقة في البداية
250
+ if len(self.performance_history) <= 10: # أول 10 صفقات
251
+ await self.adapt_weights_based_on_performance()
252
+ await self.save_weights_to_r2()
253
+ await self.save_performance_history()
254
+ else:
255
+ # بعد ذلك، تحديث كل 3 صفقات
256
+ if len(self.performance_history) % 3 == 0:
257
+ await self.adapt_weights_based_on_performance()
258
+ await self.save_weights_to_r2()
259
+ await self.save_performance_history()
260
+
261
+ print(f"📊 تم تحليل صفقة {trade_data.get('symbol')} - الاستراتيجية: {strategy} - النتيجة: {outcome} - PnL: {trade_data.get('pnl_percent', 0):.2f}%")
262
+
263
+ except Exception as e:
264
+ print(f"❌ فشل تحليل نتيجة الصفقة: {e}")
265
+
266
+ async def update_strategy_effectiveness(self, analysis_entry):
267
+ """تحديث فعالية الاستراتيجيات"""
268
+ strategy = analysis_entry['strategy_used']
269
+ outcome = analysis_entry['outcome']
270
+ market_condition = analysis_entry['market_conditions']['current_trend']
271
+ pnl_percent = analysis_entry.get('pnl_percent', 0)
272
+
273
+ if strategy not in self.strategy_effectiveness:
274
+ self.strategy_effectiveness[strategy] = {
275
+ "total_trades": 0,
276
+ "successful_trades": 0,
277
+ "total_profit": 0,
278
+ "total_pnl_percent": 0,
279
+ "market_conditions": {}
280
+ }
281
+
282
+ self.strategy_effectiveness[strategy]["total_trades"] += 1
283
+ self.strategy_effectiveness[strategy]["total_pnl_percent"] += pnl_percent
284
+
285
+ # تحديد النجاح بناءً على النتيجة والأداء
286
+ is_success = outcome in ["SUCCESS", "CLOSED_BY_REANALYSIS", "CLOSED_BY_MONITOR"] and pnl_percent > 0
287
+ if is_success:
288
+ self.strategy_effectiveness[strategy]["successful_trades"] += 1
289
+
290
+ if market_condition not in self.strategy_effectiveness[strategy]["market_conditions"]:
291
+ self.strategy_effectiveness[strategy]["market_conditions"][market_condition] = {
292
+ "trades": 0,
293
+ "successes": 0,
294
+ "total_pnl": 0
295
+ }
296
+
297
+ self.strategy_effectiveness[strategy]["market_conditions"][market_condition]["trades"] += 1
298
+ self.strategy_effectiveness[strategy]["market_conditions"][market_condition]["total_pnl"] += pnl_percent
299
+ if is_success:
300
+ self.strategy_effectiveness[strategy]["market_conditions"][market_condition]["successes"] += 1
301
+
302
+ async def update_market_patterns(self, analysis_entry):
303
+ """تحديث أنماط السوق"""
304
+ market_condition = analysis_entry['market_conditions']['current_trend']
305
+ symbol = analysis_entry['symbol']
306
+ outcome = analysis_entry['outcome']
307
+ pnl_percent = analysis_entry.get('pnl_percent', 0)
308
+
309
+ if market_condition not in self.market_patterns:
310
+ self.market_patterns[market_condition] = {
311
+ "total_trades": 0,
312
+ "successful_trades": 0,
313
+ "total_pnl_percent": 0,
314
+ "best_performing_strategies": {},
315
+ "best_performing_symbols": {}
316
+ }
317
+
318
+ self.market_patterns[market_condition]["total_trades"] += 1
319
+ self.market_patterns[market_condition]["total_pnl_percent"] += pnl_percent
320
+
321
+ is_success = outcome in ["SUCCESS", "CLOSED_BY_REANALYSIS", "CLOSED_BY_MONITOR"] and pnl_percent > 0
322
+ if is_success:
323
+ self.market_patterns[market_condition]["successful_trades"] += 1
324
+
325
+ strategy = analysis_entry['strategy_used']
326
+ if strategy not in self.market_patterns[market_condition]["best_performing_strategies"]:
327
+ self.market_patterns[market_condition]["best_performing_strategies"][strategy] = {
328
+ "count": 0,
329
+ "total_pnl": 0
330
+ }
331
+
332
+ self.market_patterns[market_condition]["best_performing_strategies"][strategy]["count"] += 1
333
+ self.market_patterns[market_condition]["best_performing_strategies"][strategy]["total_pnl"] += pnl_percent
334
+
335
+ if symbol not in self.market_patterns[market_condition]["best_performing_symbols"]:
336
+ self.market_patterns[market_condition]["best_performing_symbols"][symbol] = {
337
+ "count": 0,
338
+ "total_pnl": 0
339
+ }
340
+
341
+ self.market_patterns[market_condition]["best_performing_symbols"][symbol]["count"] += 1
342
+ self.market_patterns[market_condition]["best_performing_symbols"][symbol]["total_pnl"] += pnl_percent
343
+
344
+ async def adapt_weights_based_on_performance(self):
345
+ """تعديل الأوزان بناءً على الأداء - الإصدار المحسّن"""
346
+ print("🔄 تحديث الأوزان بناءً على الأداء...")
347
+
348
+ try:
349
+ # إذا لم تكن هناك بيانات كافية، استخدم تحديثاً تدريجياً
350
+ if not self.strategy_effectiveness:
351
+ print("⚠️ لا توجد بيانات أداء كافية، استخدام تحديث تدريجي")
352
+ await self.gradual_weights_adjustment()
353
+ return
354
+
355
+ # تحديث أوزان الاستراتيجيات بناءً على الأداء الحقيقي
356
+ total_performance = 0
357
+ strategy_performance = {}
358
+
359
+ for strategy, data in self.strategy_effectiveness.items():
360
+ if data["total_trades"] > 0:
361
+ success_rate = data["successful_trades"] / data["total_trades"]
362
+ avg_pnl = data["total_pnl_percent"] / data["total_trades"]
363
+
364
+ # حساب الأداء المركب
365
+ composite_performance = (success_rate * 0.7) + (min(avg_pnl, 10) / 10 * 0.3)
366
+ strategy_performance[strategy] = composite_performance
367
+ total_performance += composite_performance
368
+
369
+ # إذا كان هناك أداء كافٍ، قم بالتحديث
370
+ if total_performance > 0 and strategy_performance:
371
+ for strategy, performance in strategy_performance.items():
372
+ current_weight = self.weights["strategy_weights"].get(strategy, 0.1)
373
+ # تحديث تدريجي لتجنب التغيرات المفاجئة
374
+ new_weight = current_weight * 0.7 + (performance * 0.3)
375
+ self.weights["strategy_weights"][strategy] = new_weight
376
+
377
+ # تطبيع الأوزان
378
+ self.normalize_weights()
379
+ print("✅ تم تحديث الأوزان بناءً على الأداء الحقيقي")
380
+ else:
381
+ await self.gradual_weights_adjustment()
382
+
383
+ except Exception as e:
384
+ print(f"❌ فشل تحديث الأوزان: {e}")
385
+ await self.gradual_weights_adjustment()
386
+
387
+ async def gradual_weights_adjustment(self):
388
+ """تعديل تدريجي للأوزان لتحسين الأداء"""
389
+ print("📈 إجراء تعديل تدريجي على الأوزان...")
390
+
391
+ # زيادة وزن الاستراتيجيات التي تعتمد على البيانات المتاحة
392
+ if self.market_patterns:
393
+ for market_condition, data in self.market_patterns.items():
394
+ if data.get("total_trades", 0) > 0:
395
+ best_strategy = max(data["best_performing_strategies"].items(),
396
+ key=lambda x: x[1]["total_pnl"])[0] if data["best_performing_strategies"] else None
397
+ if best_strategy:
398
+ current_weight = self.weights["strategy_weights"].get(best_strategy, 0.1)
399
+ self.weights["strategy_weights"][best_strategy] = min(current_weight * 1.1, 0.3)
400
+
401
+ self.normalize_weights()
402
+ print("✅ تم التعديل التدريجي للأوزان")
403
+
404
+ def normalize_weights(self):
405
+ """تطبيع الأوزان للتأكد من أن مجموعها 1"""
406
+ total = sum(self.weights["strategy_weights"].values())
407
+ if total > 0:
408
+ for strategy in self.weights["strategy_weights"]:
409
+ self.weights["strategy_weights"][strategy] /= total
410
+
411
+ async def get_current_market_conditions(self):
412
+ """الحصول على ظروف السوق الحالية - بيانات حقيقية"""
413
+ try:
414
+ if not self.data_manager:
415
+ raise ValueError("DataManager غير متوفر")
416
+
417
+ market_context = await self.data_manager.get_market_context_async()
418
+ if not market_context:
419
+ raise ValueError("فشل جلب سياق السوق")
420
+
421
+ return {
422
+ "current_trend": market_context.get('market_trend', 'sideways_market'),
423
+ "volatility": self._calculate_market_volatility(market_context),
424
+ "market_sentiment": market_context.get('btc_sentiment', 'NEUTRAL'),
425
+ "whale_activity": market_context.get('general_whale_activity', {}).get('sentiment', 'NEUTRAL'),
426
+ "fear_greed_index": market_context.get('fear_and_greed_index', 50)
427
+ }
428
+ except Exception as e:
429
+ print(f"⚠️ فشل الحصول على ظروف السوق: {e}")
430
+ return {
431
+ "current_trend": "sideways_market",
432
+ "volatility": "medium",
433
+ "market_sentiment": "neutral",
434
+ "whale_activity": "low",
435
+ "fear_greed_index": 50
436
+ }
437
+
438
+ def _calculate_market_volatility(self, market_context):
439
+ """حساب تقلبية السوق بناءً على البيانات الحقيقية"""
440
+ try:
441
+ btc_price = market_context.get('bitcoin_price_usd', 0)
442
+ fear_greed = market_context.get('fear_and_greed_index', 50)
443
+ whale_sentiment = market_context.get('general_whale_activity', {}).get('sentiment', 'NEUTRAL')
444
+
445
+ volatility_score = 0
446
+
447
+ # تحليل سعر البيتكوين (تغيرات كبيرة = تقلبية عالية)
448
+ if btc_price > 0:
449
+ # هذا مؤشر مبسط - في التطبيق الحقيقي نحتاج بيانات تاريخية
450
+ if abs(fear_greed - 50) > 20:
451
+ volatility_score += 1
452
+
453
+ # تحليل نشاط الحيتان
454
+ if whale_sentiment in ['BULLISH', 'BEARISH']:
455
+ volatility_score += 1
456
+ elif whale_sentiment == 'SLIGHTLY_BULLISH':
457
+ volatility_score += 0.5
458
+
459
+ if volatility_score >= 1.5:
460
+ return "high"
461
+ elif volatility_score >= 0.5:
462
+ return "medium"
463
+ else:
464
+ return "low"
465
+
466
+ except Exception as e:
467
+ print(f"⚠️ خطأ في حساب التقلبية: {e}")
468
+ return "medium"
469
+
470
+ async def calculate_performance_metrics(self):
471
+ """حساب مقاييس الأداء"""
472
+ if not self.performance_history:
473
+ return {"status": "لا توجد بيانات أداء بعد"}
474
+
475
+ recent_trades = self.performance_history[-50:] # آخر 50 صفقة فقط
476
+
477
+ total_trades = len(recent_trades)
478
+ successful_trades = sum(1 for trade in recent_trades
479
+ if trade['outcome'] in ["SUCCESS", "CLOSED_BY_REANALYSIS", "CLOSED_BY_MONITOR"] and trade.get('pnl_percent', 0) > 0)
480
+ success_rate = successful_trades / total_trades if total_trades > 0 else 0
481
+
482
+ total_pnl = sum(trade.get('pnl_percent', 0) for trade in recent_trades)
483
+ avg_pnl = total_pnl / total_trades if total_trades > 0 else 0
484
+
485
+ strategy_performance = {}
486
+ for strategy, data in self.strategy_effectiveness.items():
487
+ if data["total_trades"] > 0:
488
+ strategy_success_rate = data["successful_trades"] / data["total_trades"]
489
+ strategy_avg_pnl = data["total_pnl_percent"] / data["total_trades"]
490
+ strategy_performance[strategy] = {
491
+ "success_rate": strategy_success_rate,
492
+ "avg_pnl_percent": strategy_avg_pnl,
493
+ "total_trades": data["total_trades"],
494
+ "successful_trades": data["successful_trades"]
495
+ }
496
+
497
+ market_performance = {}
498
+ for condition, data in self.market_patterns.items():
499
+ if data["total_trades"] > 0:
500
+ market_success_rate = data["successful_trades"] / data["total_trades"]
501
+ market_avg_pnl = data["total_pnl_percent"] / data["total_trades"]
502
+ market_performance[condition] = {
503
+ "success_rate": market_success_rate,
504
+ "avg_pnl_percent": market_avg_pnl,
505
+ "total_trades": data["total_trades"]
506
+ }
507
+
508
+ return {
509
+ "overall_success_rate": success_rate,
510
+ "overall_avg_pnl_percent": avg_pnl,
511
+ "total_analyzed_trades": len(self.performance_history),
512
+ "recent_trades_analyzed": total_trades,
513
+ "strategy_performance": strategy_performance,
514
+ "market_performance": market_performance,
515
+ "last_updated": datetime.now().isoformat()
516
+ }
517
+
518
+ async def get_optimized_strategy_weights(self, market_condition):
519
+ """الحصول على أوزان استراتيجية محسنة - الإصدار المصحح"""
520
+ try:
521
+ if not self.initialized:
522
+ print("⚠️ نظام التعلم غير مهيء، استخدام الأوزان الافتراضية")
523
+ return await self.get_default_strategy_weights()
524
+
525
+ # ✅ التحقق من وجود الأوزان وهيكلتها بشكل صحيح
526
+ if (not self.weights or
527
+ "strategy_weights" not in self.weights or
528
+ not self.weights["strategy_weights"]):
529
+ print("⚠️ الأوزان غير متوفرة أو فارغة، استخدام الأوزان الافتراضية")
530
+ return await self.get_default_strategy_weights()
531
+
532
+ base_weights = self.weights["strategy_weights"].copy()
533
+
534
+ # ✅ التحقق من أن الأوزان تحتوي على استراتيجيات فعلية
535
+ if not any(weight > 0 for weight in base_weights.values()):
536
+ print("⚠️ جميع الأوزان صفر، استخدام الأوزان الافتراضية")
537
+ return await self.get_default_strategy_weights()
538
+
539
+ print(f"✅ استخدام الأوزان المتعلمة: {base_weights}")
540
+ return base_weights
541
+
542
+ except Exception as e:
543
+ print(f"❌ فشل في حساب الأوزان المحسنة: {e}")
544
+ return await self.get_default_strategy_weights()
545
+
546
+ async def get_default_strategy_weights(self):
547
+ """إرجاع الأوزان الافتراضية"""
548
+ return {
549
+ "trend_following": 0.18,
550
+ "mean_reversion": 0.15,
551
+ "breakout_momentum": 0.22,
552
+ "volume_spike": 0.12,
553
+ "whale_tracking": 0.15,
554
+ "pattern_recognition": 0.10,
555
+ "hybrid_ai": 0.08
556
+ }
557
+
558
+ async def get_risk_parameters(self, symbol_volatility):
559
+ """الحصول على معايير المخاطرة المحسنة"""
560
+ if not self.weights or "risk_parameters" not in self.weights:
561
+ await self.initialize_default_weights()
562
+
563
+ risk_params = self.weights.get("risk_parameters", {}).copy()
564
+
565
+ # تعديل معايير المخاطرة بناءً على تقلبية الرمز
566
+ if symbol_volatility == "HIGH":
567
+ risk_params["stop_loss_base"] *= 1.5
568
+ risk_params["max_position_size"] *= 0.7
569
+ risk_params["risk_reward_ratio"] = 1.5 # تخفيض نسبة المكافأة/المخاطرة للتقليل العالي
570
+ elif symbol_volatility == "LOW":
571
+ risk_params["stop_loss_base"] *= 0.7
572
+ risk_params["max_position_size"] *= 1.2
573
+ risk_params["risk_reward_ratio"] = 2.5 # زيادة النسبة للتقليل المنخفض
574
+
575
+ return risk_params
576
+
577
+ async def suggest_improvements(self):
578
+ """اقتراح تحسينات بناءً على تحليل الأداء"""
579
+ improvements = []
580
+
581
+ if not self.performance_history:
582
+ improvements.append("📊 ابدأ بجمع بيانات الأداء من الصفقات الأولى")
583
+ return improvements
584
+
585
+ # تحليل أداء الاستراتيجيات
586
+ for strategy, data in self.strategy_effectiveness.items():
587
+ if data["total_trades"] >= 3:
588
+ success_rate = data["successful_trades"] / data["total_trades"]
589
+ avg_pnl = data["total_pnl_percent"] / data["total_trades"]
590
+
591
+ if success_rate < 0.3 and avg_pnl < 0:
592
+ improvements.append(f"🚨 استراتيجية {strategy} ضعيفة الأداء ({success_rate:.1%} نجاح، {avg_pnl:+.1f}% متوسط) - يقترح تقليل استخدامها")
593
+ elif success_rate > 0.6 and avg_pnl > 2:
594
+ improvements.append(f"✅ استراتيجية {strategy} ممتازة الأداء ({success_rate:.1%} نجاح، {avg_pnl:+.1f}% متوسط) - يقترح زيادة استخدامها")
595
+ elif success_rate > 0.7:
596
+ improvements.append(f"🎯 استراتيجية {strategy} عالية النجاح ({success_rate:.1%}) - التركيز على جودة الصفقات")
597
+
598
+ # تحليل أداء ظروف السوق
599
+ for market_condition, data in self.market_patterns.items():
600
+ if data["total_trades"] >= 5:
601
+ success_rate = data["successful_trades"] / data["total_trades"]
602
+ avg_pnl = data["total_pnl_percent"] / data["total_trades"]
603
+
604
+ if success_rate < 0.4:
605
+ improvements.append(f"⚠️ الأداء ضعيف في سوق {market_condition} ({success_rate:.1%} نجاح) - يحتاج مراجعة الاستراتيجيات")
606
+
607
+ # العثور على أفضل استراتيجية لهذا السوق
608
+ best_strategy = None
609
+ best_performance = -100
610
+ for strategy, stats in data["best_performing_strategies"].items():
611
+ if stats["count"] >= 2:
612
+ strategy_avg_pnl = stats["total_pnl"] / stats["count"]
613
+ if strategy_avg_pnl > best_performance:
614
+ best_performance = strategy_avg_pnl
615
+ best_strategy = strategy
616
+
617
+ if best_strategy and best_performance > 1:
618
+ improvements.append(f"📈 أفضل استراتيجية في {market_condition}: {best_strategy} ({best_performance:+.1f}% متوسط ربح)")
619
+
620
+ if not improvements:
621
+ improvements.append("📊 لا توجد تحسينات مقترحة حالياً - استمر في جمع البيانات")
622
+
623
+ return improvements
624
+
625
+ async def force_strategy_learning(self):
626
+ """إجبار النظام على التعلم من البيانات الحالية"""
627
+ print("🧠 إجبار تحديث الاستراتيجيات من البيانات الحالية...")
628
+
629
+ if not self.performance_history:
630
+ print("⚠️ لا توجد بيانات أداء للتعلم منها")
631
+ return
632
+
633
+ # تحديث فعالية الاستراتيجيات من البيانات التاريخية
634
+ for entry in self.performance_history:
635
+ await self.update_strategy_effectiveness(entry)
636
+ await self.update_market_patterns(entry)
637
+
638
+ # تحديث الأوزان فوراً
639
+ await self.adapt_weights_based_on_performance()
640
+ await self.save_weights_to_r2()
641
+
642
+ print("✅ تم إجبار تحديث الاستراتيجيات بنجاح")
643
+
644
+ print("✅ نظام التعلم الذاتي المحسن محمل - جاهز للتعلم والتكيف المستمر")
r2 (29).py ADDED
@@ -0,0 +1,648 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # r2 (23).py - الإصلاحات
2
+ import os
3
+ import traceback
4
+ import json
5
+ from datetime import datetime, timedelta
6
+ import asyncio
7
+ import time
8
+ import boto3
9
+ from botocore.exceptions import NoCredentialsError, ClientError
10
+
11
+ # --- R2 Service Configuration ---
12
+ R2_ACCOUNT_ID = os.getenv("R2_ACCOUNT_ID")
13
+ R2_ACCESS_KEY_ID = os.getenv("R2_ACCESS_KEY_ID")
14
+ R2_SECRET_ACCESS_KEY = os.getenv("R2_SECRET_ACCESS_KEY")
15
+ BUCKET_NAME = "trading"
16
+ INITIAL_CAPITAL = 10.0
17
+
18
+ class R2Service:
19
+ def __init__(self):
20
+ try:
21
+ endpoint_url = f"https://{R2_ACCOUNT_ID}.r2.cloudflarestorage.com"
22
+ self.s3_client = boto3.client(
23
+ 's3',
24
+ endpoint_url=endpoint_url,
25
+ aws_access_key_id=R2_ACCESS_KEY_ID,
26
+ aws_secret_access_key=R2_SECRET_ACCESS_KEY,
27
+ )
28
+ self.lock_acquired = False
29
+ self.BUCKET_NAME = BUCKET_NAME
30
+
31
+ self._open_trades_warning_printed = False
32
+ self._portfolio_warning_printed = False
33
+ self._contracts_warning_printed = False
34
+
35
+ except Exception as e:
36
+ raise RuntimeError(f"Failed to initialize S3 client: {e}")
37
+
38
+ def acquire_lock(self, max_retries=3):
39
+ """Acquires a lock file in R2 with retry logic."""
40
+ lock_path = "lock.txt"
41
+
42
+ for attempt in range(max_retries):
43
+ try:
44
+ try:
45
+ self.s3_client.head_object(Bucket=BUCKET_NAME, Key=lock_path)
46
+ print(f"🔒 Lock file exists. Attempt {attempt + 1}/{max_retries}. Waiting...")
47
+ time.sleep(1)
48
+ except ClientError as e:
49
+ if e.response['Error']['Code'] == '404':
50
+ self.s3_client.put_object(Bucket=BUCKET_NAME, Key=lock_path, Body=b'')
51
+ self.lock_acquired = True
52
+ print("✅ Lock acquired.")
53
+ return True
54
+ else:
55
+ raise
56
+ except Exception as e:
57
+ print(f"❌ Failed to acquire lock: {e}")
58
+ time.sleep(1)
59
+
60
+ print(f"❌ Failed to acquire lock after {max_retries} attempts.")
61
+ return False
62
+
63
+ def release_lock(self):
64
+ """Releases the lock file from R2."""
65
+ lock_path = "lock.txt"
66
+ if self.lock_acquired:
67
+ try:
68
+ self.s3_client.delete_object(Bucket=BUCKET_NAME, Key=lock_path)
69
+ print("✅ Lock released.")
70
+ self.lock_acquired = False
71
+ except Exception as e:
72
+ print(f"❌ Failed to release lock: {e}")
73
+
74
+ async def close_trade_async(self, trade_to_close, close_price):
75
+ """Closes a trade, archives it, updates summary, and updates portfolio capital."""
76
+ try:
77
+ trade_to_close['status'] = 'CLOSED'
78
+ trade_to_close['close_price'] = close_price
79
+ trade_to_close['close_timestamp'] = datetime.now().isoformat()
80
+ trade_to_close['is_monitored'] = False
81
+
82
+ entry_price = trade_to_close['entry_price']
83
+ position_size = trade_to_close['position_size_usd']
84
+ trade_type = trade_to_close.get('trade_type', 'LONG')
85
+ strategy = trade_to_close.get('strategy', 'unknown')
86
+
87
+ # ✅ الإصلاح: حساب PnL بشكل صحيح
88
+ pnl = 0.0
89
+ pnl_percent = 0.0
90
+
91
+ if entry_price and entry_price > 0 and close_price and close_price > 0:
92
+ try:
93
+ if trade_type == 'LONG':
94
+ pnl_percent = ((close_price - entry_price) / entry_price) * 100
95
+ pnl = position_size * (pnl_percent / 100)
96
+ elif trade_type == 'SHORT':
97
+ pnl_percent = ((entry_price - close_price) / entry_price) * 100
98
+ pnl = position_size * (pnl_percent / 100)
99
+
100
+ print(f"💰 PnL Calculation: Entry=${entry_price:.6f}, Close=${close_price:.6f}, "
101
+ f"Position=${position_size:.2f}, Type={trade_type}, "
102
+ f"PnL=${pnl:.4f} ({pnl_percent:+.4f}%)")
103
+
104
+ except (TypeError, ZeroDivisionError) as calc_error:
105
+ print(f"⚠️ PnL calculation error: {calc_error}")
106
+ pnl = 0.0
107
+ pnl_percent = 0.0
108
+ else:
109
+ print(f"⚠️ Invalid prices for PnL calculation: Entry={entry_price}, Close={close_price}")
110
+
111
+ trade_to_close['pnl_usd'] = pnl
112
+ trade_to_close['pnl_percent'] = pnl_percent
113
+
114
+ await self._archive_closed_trade_async(trade_to_close)
115
+ await self._update_trade_summary_async(trade_to_close)
116
+
117
+ # ✅ الإصلاح: تحديث رأس المال بشكل صحيح
118
+ portfolio_state = await self.get_portfolio_state_async()
119
+ current_capital = portfolio_state.get("current_capital_usd", 0)
120
+ invested_capital = portfolio_state.get("invested_capital_usd", 0)
121
+
122
+ # حساب رأس المال الجديد
123
+ new_capital = current_capital + position_size + pnl
124
+
125
+ portfolio_state["current_capital_usd"] = new_capital
126
+ portfolio_state["invested_capital_usd"] = 0.0
127
+
128
+ if pnl > 0:
129
+ portfolio_state["winning_trades"] = portfolio_state.get("winning_trades", 0) + 1
130
+ portfolio_state["total_profit_usd"] = portfolio_state.get("total_profit_usd", 0.0) + pnl
131
+ elif pnl < 0:
132
+ portfolio_state["total_loss_usd"] = portfolio_state.get("total_loss_usd", 0.0) + abs(pnl)
133
+
134
+ await self.save_portfolio_state_async(portfolio_state)
135
+
136
+ print(f"📈 Trade PnL: ${pnl:.4f} ({pnl_percent:+.4f}%). "
137
+ f"New available capital: ${new_capital:.4f}. Strategy: {strategy}")
138
+
139
+ open_trades = await self.get_open_trades_async()
140
+ trades_to_keep = [t for t in open_trades if t.get('id') != trade_to_close.get('id')]
141
+ await self.save_open_trades_async(trades_to_keep)
142
+
143
+ print(f"✅ Trade for {trade_to_close.get('symbol')} closed and archived successfully. Strategy: {strategy}")
144
+
145
+ await self.save_system_logs_async({
146
+ "trade_closed": True,
147
+ "symbol": trade_to_close.get('symbol'),
148
+ "entry_price": entry_price,
149
+ "close_price": close_price,
150
+ "pnl_usd": pnl,
151
+ "pnl_percent": pnl_percent,
152
+ "new_capital": new_capital,
153
+ "strategy": strategy,
154
+ "position_size": position_size,
155
+ "trade_type": trade_type
156
+ })
157
+
158
+ except Exception as e:
159
+ print(f"❌ Failed to close trade: {e}")
160
+ traceback.print_exc()
161
+ raise
162
+
163
+ # باقي الدوال تبقى كما هي بدون تغيير
164
+ async def save_candidates_data_async(self, candidates_data, reanalysis_data):
165
+ """حفظ بيانات المرشحين العشرة وبيانات إعادة التحليل"""
166
+ try:
167
+ key = "candidates_data.json"
168
+ data = {
169
+ "timestamp": datetime.now().isoformat(),
170
+ "top_candidates": candidates_data,
171
+ "reanalysis_data": reanalysis_data
172
+ }
173
+
174
+ data_json = json.dumps(data, indent=2, ensure_ascii=False).encode('utf-8')
175
+ self.s3_client.put_object(
176
+ Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
177
+ )
178
+ print(f"✅ Candidates data saved to R2: {len(candidates_data) if candidates_data else 0} candidates")
179
+ except Exception as e:
180
+ print(f"❌ Failed to save candidates data: {e}")
181
+
182
+ async def save_llm_responses_async(self, symbol, prompt, full_response, parsed_decision):
183
+ """حفظ إجابات النموذج الكاملة"""
184
+ try:
185
+ key = "llm_responses.json"
186
+ try:
187
+ response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
188
+ existing_data = json.loads(response['Body'].read())
189
+ except ClientError as e:
190
+ if e.response['Error']['Code'] == 'NoSuchKey':
191
+ existing_data = {"responses": []}
192
+ else:
193
+ raise
194
+
195
+ new_response = {
196
+ "timestamp": datetime.now().isoformat(),
197
+ "symbol": symbol,
198
+ "prompt": prompt[:2000] + "..." if len(prompt) > 2000 else prompt,
199
+ "full_response": full_response,
200
+ "parsed_decision": parsed_decision
201
+ }
202
+
203
+ existing_data["responses"].append(new_response)
204
+
205
+ if len(existing_data["responses"]) > 1000:
206
+ existing_data["responses"] = existing_data["responses"][-1000:]
207
+
208
+ data_json = json.dumps(existing_data, indent=2, ensure_ascii=False).encode('utf-8')
209
+ self.s3_client.put_object(
210
+ Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
211
+ )
212
+ print(f"✅ LLM response saved for {symbol}")
213
+ except Exception as e:
214
+ print(f"❌ Failed to save LLM response: {e}")
215
+
216
+ async def save_system_logs_async(self, log_data):
217
+ """حفظ سجلات النظام"""
218
+ try:
219
+ key = "system_logs.json"
220
+ try:
221
+ response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
222
+ existing_logs = json.loads(response['Body'].read())
223
+ except ClientError as e:
224
+ if e.response['Error']['Code'] == 'NoSuchKey':
225
+ existing_logs = {"logs": []}
226
+ else:
227
+ raise
228
+
229
+ log_entry = {
230
+ "timestamp": datetime.now().isoformat(),
231
+ **log_data
232
+ }
233
+
234
+ existing_logs["logs"].append(log_entry)
235
+
236
+ if len(existing_logs["logs"]) > 2000:
237
+ existing_logs["logs"] = existing_logs["logs"][-2000:]
238
+
239
+ data_json = json.dumps(existing_logs, indent=2, ensure_ascii=False).encode('utf-8')
240
+ self.s3_client.put_object(
241
+ Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
242
+ )
243
+ print(f"✅ System log saved: {log_data.get('cycle_started', log_data.get('cycle_completed', 'event'))}")
244
+ except Exception as e:
245
+ print(f"❌ Failed to save system logs: {e}")
246
+
247
+ async def save_learning_data_async(self, learning_data):
248
+ """حفظ بيانات التعلم"""
249
+ try:
250
+ key = "learning_data.json"
251
+ data = {
252
+ "timestamp": datetime.now().isoformat(),
253
+ "learning_data": learning_data
254
+ }
255
+
256
+ data_json = json.dumps(data, indent=2, ensure_ascii=False).encode('utf-8')
257
+ self.s3_client.put_object(
258
+ Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
259
+ )
260
+ print("✅ Learning data saved to R2")
261
+ except Exception as e:
262
+ print(f"❌ Failed to save learning data: {e}")
263
+
264
+ async def load_learning_data_async(self):
265
+ """تحميل بيانات التعلم"""
266
+ try:
267
+ key = "learning_data.json"
268
+ response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
269
+ data = json.loads(response['Body'].read())
270
+ print("✅ Learning data loaded from R2")
271
+ return data
272
+ except ClientError as e:
273
+ if e.response['Error']['Code'] == 'NoSuchKey':
274
+ print("⚠️ No learning data found. Starting fresh.")
275
+ return {}
276
+ else:
277
+ raise
278
+
279
+ async def get_portfolio_state_async(self):
280
+ """Fetches the current portfolio state from R2, or initializes it."""
281
+ key = "portfolio_state.json"
282
+ try:
283
+ response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
284
+ state = json.loads(response['Body'].read())
285
+
286
+ if hasattr(self, '_portfolio_warning_printed'):
287
+ delattr(self, '_portfolio_warning_printed')
288
+
289
+ print(f"💰 Portfolio state loaded: Current Capital ${state.get('current_capital_usd', 0):.2f}")
290
+ return state
291
+ except ClientError as e:
292
+ if e.response['Error']['Code'] == 'NoSuchKey':
293
+ if not hasattr(self, '_portfolio_warning_printed'):
294
+ print(f"⚠️ No portfolio state file found. Initializing with ${INITIAL_CAPITAL:.2f}")
295
+ self._portfolio_warning_printed = True
296
+
297
+ initial_state = {
298
+ "current_capital_usd": INITIAL_CAPITAL,
299
+ "invested_capital_usd": 0.0,
300
+ "initial_capital_usd": INITIAL_CAPITAL,
301
+ "total_trades": 0,
302
+ "winning_trades": 0,
303
+ "total_profit_usd": 0.0,
304
+ "total_loss_usd": 0.0
305
+ }
306
+ await self.save_portfolio_state_async(initial_state)
307
+ return initial_state
308
+ else:
309
+ raise
310
+
311
+ async def save_portfolio_state_async(self, state):
312
+ """Saves the portfolio state to R2."""
313
+ key = "portfolio_state.json"
314
+ try:
315
+ data_json = json.dumps(state, indent=2).encode('utf-8')
316
+ self.s3_client.put_object(
317
+ Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
318
+ )
319
+ print(f"💾 Portfolio state saved: Current Capital ${state.get('current_capital_usd', 0):.2f}")
320
+ except Exception as e:
321
+ print(f"❌ Failed to save portfolio state: {e}")
322
+ raise
323
+
324
+ async def get_open_trades_async(self):
325
+ """Fetches all open trades from R2 with reduced noise."""
326
+ try:
327
+ response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key="open_trades.json")
328
+ trades = json.loads(response['Body'].read())
329
+
330
+ if hasattr(self, '_open_trades_warning_printed'):
331
+ delattr(self, '_open_trades_warning_printed')
332
+
333
+ return trades
334
+ except ClientError as e:
335
+ if e.response['Error']['Code'] == 'NoSuchKey':
336
+ if not hasattr(self, '_open_trades_warning_printed'):
337
+ print("⚠️ No open trades file found. Starting with an empty list.")
338
+ print("💡 This is normal for first-time runs or when all trades are closed.")
339
+ self._open_trades_warning_printed = True
340
+ return []
341
+ else:
342
+ raise
343
+
344
+ async def save_open_trades_async(self, trades):
345
+ """Saves the list of open trades to R2."""
346
+ try:
347
+ data_json = json.dumps(trades, indent=2).encode('utf-8')
348
+ self.s3_client.put_object(
349
+ Bucket=BUCKET_NAME, Key="open_trades.json", Body=data_json, ContentType="application/json"
350
+ )
351
+ print(f"✅ Open trades saved to R2. Total open trades: {len(trades)}")
352
+ except Exception as e:
353
+ print(f"❌ Failed to save open trades: {e}")
354
+ raise
355
+
356
+ async def save_new_trade_async(self, symbol, decision, current_price):
357
+ """Creates a new trade using the full available capital and saves it."""
358
+ try:
359
+ portfolio_state = await self.get_portfolio_state_async()
360
+ available_capital = portfolio_state.get("current_capital_usd", 0)
361
+
362
+ if available_capital < 1:
363
+ print(f"❌ Insufficient capital (${available_capital:.2f}) to open a new trade.")
364
+ return
365
+
366
+ expected_target_minutes = decision.get('expected_target_minutes', 15)
367
+ expected_target_minutes = max(5, min(expected_target_minutes, 45))
368
+ expected_target_time = (datetime.now() + timedelta(minutes=expected_target_minutes)).isoformat()
369
+
370
+ # ✅ الإصلاح النهائي: التأكد من وجود استراتيجية صالحة
371
+ strategy = decision.get('strategy')
372
+ if not strategy or strategy == 'unknown':
373
+ strategy = 'GENERIC'
374
+ print(f"⚠️ Strategy was missing or unknown. Setting to GENERIC for {symbol}")
375
+
376
+ trades = await self.get_open_trades_async()
377
+ new_trade = {
378
+ "id": str(int(time.time())),
379
+ "symbol": symbol,
380
+ "entry_price": current_price,
381
+ "entry_timestamp": datetime.now().isoformat(),
382
+ "decision_data": decision,
383
+ "status": "OPEN",
384
+ "stop_loss": decision.get("stop_loss"),
385
+ "take_profit": decision.get("take_profit"),
386
+ "trade_type": decision.get("trade_type"),
387
+ "position_size_usd": available_capital,
388
+ "expected_target_minutes": expected_target_minutes,
389
+ "expected_target_time": expected_target_time,
390
+ "is_monitored": True,
391
+ "strategy": strategy # ✅ استخدام الاستراتيجية المؤكدة
392
+ }
393
+ trades.append(new_trade)
394
+ await self.save_open_trades_async(trades)
395
+
396
+ original_expected = decision.get('expected_target_minutes', 15)
397
+ if original_expected > 45:
398
+ print(f"⚠️ LLM wanted {original_expected} minutes, CAPPED to 45 minutes for strategy consistency")
399
+
400
+ print(f"✅ New trade for {symbol} saved with position size ${available_capital:.2f}. Strategy: {strategy}. Expected results in {expected_target_minutes} minutes.")
401
+
402
+ portfolio_state["invested_capital_usd"] = available_capital
403
+ portfolio_state["current_capital_usd"] = 0.0
404
+ portfolio_state["total_trades"] = portfolio_state.get("total_trades", 0) + 1
405
+ await self.save_portfolio_state_async(portfolio_state)
406
+
407
+ await self.save_system_logs_async({
408
+ "new_trade_opened": True,
409
+ "symbol": symbol,
410
+ "position_size": available_capital,
411
+ "expected_minutes": expected_target_minutes,
412
+ "trade_type": decision.get("trade_type", "LONG"),
413
+ "strategy": strategy # ✅ استخدام الاستراتيجية المؤكدة
414
+ })
415
+
416
+ except Exception as e:
417
+ print(f"❌ Failed to save new trade: {e}")
418
+ raise
419
+
420
+ async def update_trade_async(self, trade_to_update, re_analysis_decision):
421
+ """Updates an existing trade with new parameters from re-analysis."""
422
+ try:
423
+ if re_analysis_decision.get('new_stop_loss'):
424
+ trade_to_update['stop_loss'] = re_analysis_decision['new_stop_loss']
425
+ if re_analysis_decision.get('new_take_profit'):
426
+ trade_to_update['take_profit'] = re_analysis_decision['new_take_profit']
427
+
428
+ new_expected_minutes = re_analysis_decision.get('new_expected_minutes')
429
+ if new_expected_minutes:
430
+ new_expected_minutes = max(5, min(new_expected_minutes, 45))
431
+ trade_to_update['expected_target_minutes'] = new_expected_minutes
432
+ trade_to_update['expected_target_time'] = (datetime.now() + timedelta(minutes=new_expected_minutes)).isoformat()
433
+ print(f"⏰ Trade time expectation updated to {new_expected_minutes} minutes.")
434
+
435
+ # ✅ الإصلاح: الحفاظ على الاستراتيجية الأصلية
436
+ original_strategy = trade_to_update.get('strategy')
437
+ if not original_strategy or original_strategy == 'unknown':
438
+ original_strategy = re_analysis_decision.get('strategy', 'GENERIC')
439
+
440
+ trade_to_update['strategy'] = original_strategy
441
+ trade_to_update['decision_data'] = re_analysis_decision
442
+ trade_to_update['is_monitored'] = True
443
+
444
+ open_trades = await self.get_open_trades_async()
445
+ for i, trade in enumerate(open_trades):
446
+ if trade.get('id') == trade_to_update.get('id'):
447
+ open_trades[i] = trade_to_update
448
+ break
449
+
450
+ await self.save_open_trades_async(open_trades)
451
+ print(f"✅ Trade for {trade_to_update.get('symbol')} updated successfully. Strategy: {original_strategy}")
452
+
453
+ await self.save_system_logs_async({
454
+ "trade_updated": True,
455
+ "symbol": trade_to_update.get('symbol'),
456
+ "new_expected_minutes": new_expected_minutes,
457
+ "action": "UPDATE_TRADE",
458
+ "strategy": original_strategy
459
+ })
460
+
461
+ except Exception as e:
462
+ print(f"❌ Failed to update trade: {e}")
463
+ raise
464
+
465
+ async def immediate_close_trade_async(self, symbol, close_price, reason="Real-time monitoring"):
466
+ """Closes a trade immediately without full re-analysis."""
467
+ try:
468
+ open_trades = await self.get_open_trades_async()
469
+ trade_to_close = None
470
+
471
+ for trade in open_trades:
472
+ if trade['symbol'] == symbol and trade['status'] == 'OPEN':
473
+ trade_to_close = trade
474
+ break
475
+
476
+ if not trade_to_close:
477
+ print(f"❌ No open trade found for {symbol}")
478
+ return False
479
+
480
+ await self.close_trade_async(trade_to_close, close_price)
481
+ print(f"🚨 IMMEDIATE CLOSE: {symbol} at {close_price} - {reason}")
482
+
483
+ await self.save_system_logs_async({
484
+ "immediate_close": True,
485
+ "symbol": symbol,
486
+ "close_price": close_price,
487
+ "reason": reason,
488
+ "strategy": trade_to_close.get('strategy', 'unknown')
489
+ })
490
+
491
+ return True
492
+
493
+ except Exception as e:
494
+ print(f"❌ Failed to immediately close trade {symbol}: {e}")
495
+ return False
496
+
497
+ async def _archive_closed_trade_async(self, closed_trade):
498
+ """Appends a closed trade to the history file."""
499
+ key = "closed_trades_history.json"
500
+ try:
501
+ response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
502
+ history = json.loads(response['Body'].read())
503
+ except ClientError as e:
504
+ if e.response['Error']['Code'] == 'NoSuchKey':
505
+ history = []
506
+ else:
507
+ raise
508
+
509
+ history.append(closed_trade)
510
+
511
+ data_json = json.dumps(history, indent=2).encode('utf-8')
512
+ self.s3_client.put_object(
513
+ Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
514
+ )
515
+ print(f"📚 Trade archived. Total archived trades: {len(history)}")
516
+
517
+ async def _update_trade_summary_async(self, closed_trade):
518
+ """Updates the trade summary statistics file."""
519
+ key = "trade_summary.json"
520
+ try:
521
+ try:
522
+ response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
523
+ summary = json.loads(response['Body'].read())
524
+ except ClientError as e:
525
+ if e.response['Error']['Code'] == 'NoSuchKey':
526
+ summary = {
527
+ "total_trades": 0, "winning_trades": 0, "losing_trades": 0,
528
+ "total_profit_usd": 0.0, "total_loss_usd": 0.0, "win_percentage": 0.0,
529
+ "avg_profit_per_trade": 0.0, "avg_loss_per_trade": 0.0,
530
+ "largest_win": 0.0, "largest_loss": 0.0
531
+ }
532
+ else:
533
+ raise
534
+
535
+ pnl = closed_trade.get('pnl_usd', 0.0)
536
+
537
+ summary['total_trades'] += 1
538
+ if pnl >= 0:
539
+ summary['winning_trades'] += 1
540
+ summary['total_profit_usd'] += pnl
541
+ if pnl > summary.get('largest_win', 0):
542
+ summary['largest_win'] = pnl
543
+ else:
544
+ summary['losing_trades'] += 1
545
+ summary['total_loss_usd'] += abs(pnl)
546
+ if abs(pnl) > summary.get('largest_loss', 0):
547
+ summary['largest_loss'] = abs(pnl)
548
+
549
+ if summary['total_trades'] > 0:
550
+ summary['win_percentage'] = (summary['winning_trades'] / summary['total_trades']) * 100
551
+
552
+ if summary['winning_trades'] > 0:
553
+ summary['avg_profit_per_trade'] = summary['total_profit_usd'] / summary['winning_trades']
554
+ if summary['losing_trades'] > 0:
555
+ summary['avg_loss_per_trade'] = summary['total_loss_usd'] / summary['losing_trades']
556
+
557
+ data_json = json.dumps(summary, indent=2).encode('utf-8')
558
+ self.s3_client.put_object(
559
+ Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
560
+ )
561
+ print(f"📊 Trade summary updated. Win Rate: {summary['win_percentage']:.2f}%")
562
+
563
+ except Exception as e:
564
+ print(f"❌ Failed to update trade summary: {e}")
565
+ raise
566
+
567
+ async def load_contracts_db_async(self):
568
+ """Loads the contracts database from R2 with reduced noise."""
569
+ key = "contracts.json"
570
+ try:
571
+ response = self.s3_client.get_object(Bucket=BUCKET_NAME, Key=key)
572
+ contracts_db = json.loads(response['Body'].read())
573
+
574
+ if hasattr(self, '_contracts_warning_printed'):
575
+ delattr(self, '_contracts_warning_printed')
576
+
577
+ print(f"💾 Contracts database loaded from R2. Total entries: {len(contracts_db)}")
578
+ return contracts_db
579
+ except ClientError as e:
580
+ if e.response['Error']['Code'] == 'NoSuchKey':
581
+ if not hasattr(self, '_contracts_warning_printed'):
582
+ print("⚠️ No existing contracts database found. Initializing new one.")
583
+ self._contracts_warning_printed = True
584
+ return {}
585
+ else:
586
+ raise
587
+
588
+ async def save_contracts_db_async(self, data):
589
+ """Saves the contracts database to R2."""
590
+ key = "contracts.json"
591
+ try:
592
+ data_json = json.dumps(data, indent=2).encode('utf-8')
593
+ self.s3_client.put_object(
594
+ Bucket=BUCKET_NAME, Key=key, Body=data_json, ContentType="application/json"
595
+ )
596
+ print(f"✅ Contracts database saved to R2 successfully. Total entries: {len(data)}")
597
+ except Exception as e:
598
+ print(f"❌ Failed to save contracts database to R2: {e}")
599
+ raise
600
+
601
+ async def get_trade_by_symbol_async(self, symbol):
602
+ """Fetches a specific trade by symbol."""
603
+ try:
604
+ open_trades = await self.get_open_trades_async()
605
+ for trade in open_trades:
606
+ if trade['symbol'] == symbol and trade['status'] == 'OPEN':
607
+ return trade
608
+ return None
609
+ except Exception as e:
610
+ print(f"❌ Failed to get trade by symbol {symbol}: {e}")
611
+ return None
612
+
613
+ async def update_trade_monitoring_status_async(self, symbol, is_monitored):
614
+ """Updates the monitoring status of a trade."""
615
+ try:
616
+ open_trades = await self.get_open_trades_async()
617
+ updated = False
618
+
619
+ for trade in open_trades:
620
+ if trade['symbol'] == symbol:
621
+ trade['is_monitored'] = is_monitored
622
+ updated = True
623
+ break
624
+
625
+ if updated:
626
+ await self.save_open_trades_async(open_trades)
627
+ status = "ENABLED" if is_monitored else "DISABLED"
628
+ print(f"✅ Real-time monitoring {status} for {symbol}")
629
+ else:
630
+ print(f"⚠️ Trade {symbol} not found for monitoring status update")
631
+
632
+ return updated
633
+
634
+ except Exception as e:
635
+ print(f"❌ Failed to update monitoring status for {symbol}: {e}")
636
+ return False
637
+
638
+ async def get_monitored_trades_async(self):
639
+ """Fetches all trades that are currently being monitored."""
640
+ try:
641
+ open_trades = await self.get_open_trades_async()
642
+ monitored_trades = [trade for trade in open_trades if trade.get('is_monitored', False)]
643
+ return monitored_trades
644
+ except Exception as e:
645
+ print(f"❌ Failed to get monitored trades: {e}")
646
+ return []
647
+
648
+ print("✅ Enhanced R2 Service Loaded - Comprehensive Logging System with Learning Support")