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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +147 -723
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py - الإصدار المحدث مع إصلاح مشاكل إغلاق الاتصالات
2
  import os
3
  import traceback
4
  import signal
@@ -19,9 +19,7 @@ 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
@@ -35,16 +33,13 @@ 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:
@@ -55,19 +50,15 @@ class RealTimeTradeMonitor:
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)
@@ -83,864 +74,297 @@ class RealTimeTradeMonitor:
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
- print(f"🎯 Starting 3-stage filtering for {strategy} strategy...")
340
-
341
- try:
342
- # المرحلة الأولى: الفلتر الأولي السريع (2 ثانية)
343
- print("🚀 Stage 1: Snapshot Filter (2 seconds)")
344
- stage1_start = time.time()
345
- initial_candidates = await data_manager_global.fast_snapshot_filter(scan_count * 3) # جلب مرشحين أكثر للمراحل التالية
346
-
347
- if not initial_candidates:
348
- print("❌ No candidates passed Stage 1 filtering")
349
- return []
350
-
351
- stage1_time = time.time() - stage1_start
352
- print(f"✅ Stage 1 completed: {len(initial_candidates)} candidates in {stage1_time:.2f}s")
353
-
354
- # المرحلة الثانية: فلتر المؤشرات الخفيفة (5-10 ثوان)
355
- print("⚡ Stage 2: Lightweight Indicator Filter (5-10 seconds)")
356
- stage2_start = time.time()
357
- stage2_candidates = await data_manager_global.lightweight_indicator_filter(initial_candidates)
358
-
359
- if not stage2_candidates:
360
- print("❌ No candidates passed Stage 2 filtering")
361
- return []
362
-
363
- stage2_time = time.time() - stage2_start
364
- print(f"✅ Stage 2 completed: {len(stage2_candidates)} candidates in {stage2_time:.2f}s")
365
-
366
- # المرحلة الثالثة: التحليل العميق (للأفضل فقط)
367
- print("🔍 Stage 3: Deep Analysis (Top candidates only)")
368
-
369
- # تحديث market_context قبل المعالجة
370
- market_context = await data_manager_global.get_market_context_async()
371
- if not market_context:
372
- print("❌ Failed to get market context for strategy analysis")
373
- return []
374
-
375
- feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
376
-
377
- processed_candidates = []
378
- for candidate in stage2_candidates[:15]: # معالجة أفضل 15 مرشح فقط
379
- try:
380
- symbol_with_reasons = [{'symbol': candidate['symbol'], 'reasons': candidate.get('reasons', [])}]
381
- ohlcv_data = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
382
-
383
- if ohlcv_data and ohlcv_data[0]:
384
- # ✅ تحديث market_context قبل كل معالجة
385
- try:
386
- updated_market_context = await data_manager_global.get_market_context_async()
387
- if updated_market_context:
388
- feature_processor.market_context = updated_market_context
389
- except Exception as e:
390
- print(f"⚠️ Failed to update market context for {candidate['symbol']}: {e}")
391
-
392
- processed = await feature_processor.process_and_score_symbol_enhanced(ohlcv_data[0])
393
- if processed:
394
- processed_candidates.append(processed)
395
- except Exception as e:
396
- print(f"⚠️ فشل معالجة {candidate.get('symbol')}: {e}")
397
-
398
- if not processed_candidates:
399
- print("⚠️ لم يتم معالجة أي مرشح بنجاح في المرحلة الثالثة")
400
- return []
401
-
402
- # تحديد أفضل المرشحين للاستراتيجية المحددة
403
- if strategy != 'GENERIC':
404
- strategy_candidates = []
405
- for candidate in processed_candidates:
406
- base_scores = candidate.get('base_strategy_scores', {})
407
- strategy_score = base_scores.get(strategy, 0)
408
-
409
- if strategy_score > 0.3: # عتبة أعلى قليلاً للجودة
410
- candidate['strategy_match_score'] = strategy_score
411
- strategy_candidates.append(candidate)
412
- print(f"✅ {candidate['symbol']} مناسب لـ {strategy} (درجة: {strategy_score:.3f})")
413
-
414
- sorted_candidates = sorted(strategy_candidates,
415
- key=lambda x: x.get('strategy_match_score', 0),
416
- reverse=True)
417
- top_candidates = sorted_candidates[:10] # أفضل 10 فقط
418
-
419
- print(f"✅ Stage 3 completed: {len(top_candidates)} strategy-specific candidates")
420
- else:
421
- # للاستراتيجية العامة، استخدم النقاط المحسنة
422
- sorted_candidates = sorted(processed_candidates,
423
- key=lambda x: x.get('enhanced_final_score', 0),
424
- reverse=True)
425
- top_candidates = sorted_candidates[:10]
426
- print(f"✅ Stage 3 completed: {len(top_candidates)} generic strategy candidates")
427
-
428
- total_time = time.time() - stage1_start
429
- print(f"🎯 3-stage filtering completed: {len(top_candidates)} final candidates in {total_time:.2f}s")
430
-
431
- return top_candidates
432
-
433
- except Exception as error:
434
- print(f"❌ فشل في نظام الفلترة ثلاثي المراحل: {error}")
435
- traceback.print_exc()
436
- return []
437
-
438
  async def find_new_opportunities_async():
439
- """ NEW: المسح المحسن باستراتيجية مسبقة مع نظام الفلترة السريع"""
440
- print("🔍 Scanning for new opportunities with 3-stage fast filtering...")
441
  try:
442
- await r2_service_global.save_system_logs_async({
443
- "opportunity_scan_started": True, "timestamp": datetime.now().isoformat()
444
- })
445
-
446
- print("🧠 Determining trading strategy...")
447
  market_context = await data_manager_global.get_market_context_async()
448
  if not market_context:
449
- print("❌ Failed to fetch market context. Cannot determine strategy.")
450
- return
451
 
452
- strategy_decision = await analyze_market_strategy(market_context)
453
-
454
- print(f"🎯 Selected Strategy: {strategy_decision['primary_strategy']}")
455
- print(f"📝 Reasoning: {strategy_decision['reasoning']}")
456
- print(f"⚡ Risk Tolerance: {strategy_decision.get('risk_tolerance', 5)}/10")
457
- print(f"🔍 Optimal Scan Count: {strategy_decision.get('optimal_scan_count', 100)}")
458
-
459
- print(f"🔍 Finding top candidates using 3-stage filtering...")
460
- high_potential_candidates = await find_strategy_specific_candidates(
461
- strategy_decision['primary_strategy'],
462
- strategy_decision.get('optimal_scan_count', 100)
463
- )
464
 
465
- if not high_potential_candidates:
466
- print("🔄 لا توجد مرشحين متخصصين، جلب مرشحين عامين...")
467
- # ✅ استرجاع مرشحين عامين كبديل باستخدام النظام السريع أيضاً
468
- high_potential_candidates = await data_manager_global.fast_snapshot_filter(20)
469
- if high_potential_candidates:
470
- for candidate in high_potential_candidates:
471
- candidate['target_strategy'] = 'GENERIC'
472
- print(f"✅ تم تحميل {len(high_potential_candidates)} مرشح عام")
473
- else:
474
- print("✅ No new candidates found after 3-stage filtering.")
475
- await r2_service_global.save_system_logs_async({
476
- "no_candidates_found": True, "strategy": strategy_decision['primary_strategy'],
477
- "reason": "3-stage filtering did not return any candidates."
478
- })
479
- return
480
-
481
- # ✅ استخدام السياق المحدث للتصفية النهائية
482
- updated_market_context = await data_manager_global.get_market_context_async()
483
- if not updated_market_context:
484
- updated_market_context = market_context
485
 
486
- feature_processor = FeatureProcessor(updated_market_context, data_manager_global, learning_engine_global)
487
- top_candidates = feature_processor.filter_top_candidates(high_potential_candidates, OPPORTUNITY_COUNT)
488
-
489
- print(f"✅ Identified {len(top_candidates)} top candidates after final scoring.")
490
-
491
- await r2_service_global.save_candidates_data_async(
492
- candidates_data=top_candidates,
493
- reanalysis_data={"strategy_used": strategy_decision, "market_conditions": market_context}
494
- )
 
495
 
 
 
496
  if not top_candidates:
497
- print(" No strong candidates left after final filtering.")
498
- await r2_service_global.save_system_logs_async({
499
- "no_strong_candidates": True, "strategy": strategy_decision['primary_strategy'],
500
- "initial_candidates_count": len(high_potential_candidates)
501
- })
502
- return
503
 
504
- print("🧠 Getting LLM analysis for top candidates...")
505
-
506
  for candidate in top_candidates:
507
  try:
508
- if not await validate_candidate_data_enhanced(candidate):
509
- print(f"⚠️ Skipping {candidate.get('symbol')} due to quality issues")
510
- continue
511
-
512
- llm_analysis_data = await llm_service_global.get_trading_decision(candidate)
513
-
514
- if not llm_analysis_data:
515
- print(f"⚠️ LLM analysis failed for {candidate['symbol']}. Moving to next.")
516
- continue
517
-
518
- if llm_analysis_data.get('action') == "HOLD":
519
- print(f"🧠 LLM decided to HOLD on {candidate['symbol']}. Moving to next.")
520
- continue
521
-
522
- if llm_analysis_data.get('action') in ["BUY", "SELL"]:
523
- # ✅ التحقق النهائي من الاستراتيجية
524
- final_strategy = llm_analysis_data.get('strategy')
525
- candidate_strategy = candidate.get('target_strategy', 'GENERIC')
526
-
527
- # إذا كانت استراتيجية LLM غير صالحة، استخدم استراتيجية المرشح
528
- if not final_strategy or final_strategy == 'unknown' or final_strategy == 'GENERIC':
529
- final_strategy = candidate_strategy
530
- llm_analysis_data['strategy'] = final_strategy
531
- print(f"🔧 تصحيح استراتيجية LLM لـ {candidate['symbol']}: {final_strategy}")
532
-
533
- print(f"🎯 الاستراتيجية النهائية: {final_strategy}")
534
-
535
  print("\n========================================================")
536
  print(f"💎💎💎 New Trading Opportunity Identified! 💎💎💎")
537
  print(f" Symbol: {candidate['symbol']}")
538
- print(f" Action: {llm_analysis_data.get('action')}")
539
- print(f" Strategy: {final_strategy}")
540
- print(f" Reasoning: {llm_analysis_data.get('reasoning')}")
541
- print(f" Confidence: {llm_analysis_data.get('confidence_level')}")
542
  print("========================================================\n")
543
-
544
- await r2_service_global.save_system_logs_async({
545
- "new_opportunity_found": True, "symbol": candidate['symbol'],
546
- "action": llm_analysis_data.get('action'), "strategy": final_strategy,
547
- "confidence": llm_analysis_data.get('confidence_level', 0)
548
- })
549
-
550
- return {
551
- "symbol": candidate['symbol'],
552
- "decision": llm_analysis_data,
553
- "current_price": candidate['current_price'],
554
- "strategy": final_strategy
555
- }
556
-
557
- except Exception as error:
558
- print(f"❌ LLM/Fallback error for {candidate.get('symbol', 'unknown')}: {error}")
559
- traceback.print_exc()
560
 
561
- print("✅ Cycle finished. No actionable BUY/SELL opportunities found by LLM.")
562
  return None
563
-
564
  except Exception as error:
565
  print(f"❌ An error occurred while scanning for opportunities: {error}")
566
  traceback.print_exc()
567
- await r2_service_global.save_system_logs_async({"opportunity_scan_error": True, "error": str(error)})
568
  return None
569
 
570
  async def re_analyze_open_trade_async(trade_data):
571
- """Re-analyzes an open trade with enhanced strategy preservation"""
572
  symbol = trade_data.get('symbol')
573
-
574
  try:
575
- entry_time = datetime.fromisoformat(trade_data['entry_timestamp'])
576
- current_time = datetime.now()
577
- hold_minutes = (current_time - entry_time).total_seconds() / 60
578
-
579
- print(f"⏳ Re-analyzing trade: {symbol} (held for {hold_minutes:.1f} minutes)")
580
-
581
- # ✅ الإصلاح المحسن: الحفاظ على الاستراتيجية الأصلية مع التحقق الشامل
582
- original_strategy = trade_data.get('strategy')
583
- if not original_strategy or original_strategy == 'unknown':
584
- original_strategy = trade_data.get('decision_data', {}).get('strategy', 'GENERIC')
585
- print(f"🔧 Fixed original strategy for {symbol}: {original_strategy}")
586
-
587
- reanalysis_context = {
588
- 'trade_data': {
589
- 'symbol': trade_data.get('symbol'),
590
- 'entry_price': trade_data.get('entry_price'),
591
- 'entry_time': trade_data.get('entry_timestamp'),
592
- 'hold_minutes': hold_minutes,
593
- 'strategy': original_strategy
594
- }
595
- }
596
-
597
- try:
598
- market_context = await data_manager_global.get_market_context_async()
599
- except Exception as error:
600
- print(f"⚠️ Failed to get market context: {error}. Using basic market data...")
601
- market_context = {'btc_sentiment': 'NEUTRAL'}
602
-
603
- symbol_with_reasons = [{'symbol': symbol, 'reasons': ['re-analysis']}]
604
-
605
- ohlcv_data_list = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
606
- if not ohlcv_data_list:
607
- print(f"❌ Failed to fetch latest data for {symbol}.")
608
  return None
609
-
610
- raw_data = ohlcv_data_list[0]
611
- # ✅ تحديث market_context قبل المعالجة
612
- try:
613
- updated_market_context = await data_manager_global.get_market_context_async()
614
- if updated_market_context:
615
- market_context = updated_market_context
616
- except Exception as e:
617
- print(f"⚠️ Failed to update market context for re-analysis: {e}")
618
-
619
  feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
620
- processed_data = await feature_processor.process_and_score_symbol(raw_data)
621
-
622
  if not processed_data:
623
- print(f"❌ Failed to process latest data for {symbol}.")
624
  return None
625
 
626
- await r2_service_global.save_candidates_data_async(
627
- candidates_data=None,
628
- reanalysis_data={'market_context': market_context, 'processed_data': processed_data}
629
- )
630
-
631
- print(f"🧠 Getting LLM re-analysis for {symbol}...")
632
- try:
633
- re_analysis_decision = await llm_service_global.re_analyze_trade_async(trade_data, processed_data)
634
- source = re_analysis_decision.get('model_source', 'LLM')
635
- except Exception as error:
636
- print(f"❌ LLM re-analysis error: {error}. Falling back to local.")
637
- re_analysis_decision = local_re_analyze_trade(trade_data, processed_data)
638
- source = 'local_fallback'
639
-
640
- final_decision = _apply_patience_logic(re_analysis_decision, hold_minutes, trade_data, processed_data)
641
-
642
- # ✅ الإصلاح النهائي: التأكد من وجود الاستراتيجية في القرار النهائي
643
- if not final_decision.get('strategy') or final_decision['strategy'] == 'unknown':
644
- final_decision['strategy'] = original_strategy
645
- print(f"🔧 Final re-analysis strategy fix for {symbol}: {original_strategy}")
646
-
647
- print(f"✅ Re-analysis decision for {symbol}: {final_decision.get('action')}. Strategy: {final_decision.get('strategy')}. Source: {source}")
648
-
649
- await r2_service_global.save_system_logs_async({
650
- "trade_reanalyzed": True, "symbol": symbol, "action": final_decision.get('action'),
651
- "hold_minutes": hold_minutes, "source": source, "strategy": final_decision.get('strategy')
652
- })
653
-
654
- return {
655
- "symbol": symbol, "decision": final_decision,
656
- "current_price": processed_data.get('current_price'), "hold_minutes": hold_minutes
657
- }
658
-
659
  except Exception as error:
660
- print(f"❌ An error occurred during trade re-analysis: {error}")
661
  traceback.print_exc()
662
- await r2_service_global.save_system_logs_async({"reanalysis_error": True, "symbol": symbol, "error": str(error)})
663
  return None
664
 
665
- def _apply_patience_logic(decision, hold_minutes, trade_data, processed_data):
666
- """Apply patience logic to prevent premature selling decisions"""
667
- action = decision.get('action')
668
-
669
- if action == "CLOSE_TRADE" and hold_minutes < 20:
670
- current_price = processed_data.get('current_price', 0)
671
- entry_price = trade_data.get('entry_price', 0)
672
-
673
- try:
674
- profit_loss_percent = ((current_price - entry_price) / entry_price) * 100
675
- except (TypeError, ZeroDivisionError):
676
- profit_loss_percent = 0
677
-
678
- if profit_loss_percent < 2:
679
- print(f"🛑 Blocked premature selling! Only {hold_minutes:.1f} minutes held, PnL: {profit_loss_percent:.2f}%")
680
- decision['action'] = "HOLD"
681
- decision['reasoning'] = f"Patience Filter: Blocked premature sell. Held for {hold_minutes:.1f}m. Giving trade more time."
682
- return decision
683
-
684
- return decision
685
-
686
  async def run_bot_cycle_async():
687
- """The main asynchronous bot cycle with enhanced strategy validation"""
688
- print(f"\n{'='*70}")
689
- print(f"⏳ New cycle initiated at: {datetime.now().isoformat()}")
690
- print(f"{'='*70}")
691
 
692
- try:
693
- await r2_service_global.save_system_logs_async({"cycle_started": True})
694
-
695
- if not r2_service_global.acquire_lock():
696
- print("❌ Failed to acquire lock. Skipping cycle.")
697
- return
698
 
699
- open_trades = []
700
- try:
701
- open_trades = await r2_service_global.get_open_trades_async()
702
- print(f"✅ Found {len(open_trades)} open trade(s).")
703
-
704
- # ✅ الإصلاح المحسن: فحص وإصلاح الاستراتيجيات الفارغة في الصفقات المفتوحة
705
- trades_fixed = 0
706
- for trade in open_trades:
707
- if not trade.get('strategy') or trade['strategy'] == 'unknown':
708
- original_strategy = trade.get('decision_data', {}).get('strategy', 'GENERIC')
709
- trade['strategy'] = original_strategy
710
- trades_fixed += 1
711
- print(f"🔧 Fixed missing strategy for {trade['symbol']}: {trade['strategy']}")
712
-
713
- if trades_fixed > 0:
714
- print(f"✅ Fixed strategies for {trades_fixed} trades.")
715
- await r2_service_global.save_open_trades_async(open_trades)
716
-
717
- should_look_for_new_trade = not open_trades
718
-
719
- if open_trades:
720
- now = datetime.now()
721
- trades_to_reanalyze = [
722
- trade for trade in open_trades
723
- if now >= datetime.fromisoformat(trade.get('expected_target_time', now.isoformat()))
724
- ]
725
-
726
- if trades_to_reanalyze:
727
- print(f"🔍 Re-analyzing {len(trades_to_reanalyze)} trade(s)...")
728
- for trade in trades_to_reanalyze:
729
- result = await re_analyze_open_trade_async(trade)
730
- if result and result['decision'].get('action') == "CLOSE_TRADE":
731
- await r2_service_global.close_trade_async(trade, result['current_price'])
732
- print(f"✅ Trade for {trade['symbol']} CLOSED. Strategy: {trade.get('strategy', 'unknown')}")
733
- if learning_engine_global and learning_engine_global.initialized:
734
- trade_with_strategy = trade.copy()
735
- strategy = result['decision'].get('strategy', trade.get('strategy', 'GENERIC'))
736
- trade_with_strategy['strategy'] = strategy
737
- await learning_engine_global.analyze_trade_outcome(trade_with_strategy, 'CLOSED_BY_REANALYSIS')
738
- should_look_for_new_trade = True
739
- elif result and result['decision'].get('action') == "UPDATE_TRADE":
740
- await r2_service_global.update_trade_async(trade, result['decision'])
741
- print(f"✅ Trade for {trade['symbol']} UPDATED. Strategy: {trade.get('strategy', 'unknown')}")
742
- else:
743
- print(f"✅ Trade for {trade['symbol']} is on HOLD. Strategy: {trade.get('strategy', 'unknown')}")
744
- else:
745
- print("✅ No trades due for re-analysis yet.")
746
-
747
- if should_look_for_new_trade:
748
- portfolio_state = await r2_service_global.get_portfolio_state_async()
749
- if portfolio_state.get("current_capital_usd", 0) > 1:
750
- print(f"✅ Capital available (${portfolio_state['current_capital_usd']:.2f}). Scanning...")
751
- new_opportunity = await find_new_opportunities_async()
752
- if new_opportunity:
753
- print(f"✅ Opportunity for {new_opportunity['symbol']} confirmed! Saving trade. Strategy: {new_opportunity.get('strategy')}")
754
-
755
- # ✅ التحقق النهائي قبل الحفظ
756
- if not new_opportunity['decision'].get('strategy'):
757
- new_opportunity['decision']['strategy'] = new_opportunity.get('strategy', 'GENERIC')
758
- print(f"🔧 Final pre-save strategy fix: {new_opportunity['decision']['strategy']}")
759
-
760
- await r2_service_global.save_new_trade_async(
761
- new_opportunity['symbol'],
762
- new_opportunity['decision'],
763
- new_opportunity['current_price']
764
- )
765
- newly_opened_trades = await r2_service_global.get_open_trades_async()
766
- for trade in newly_opened_trades:
767
- if trade['symbol'] == new_opportunity['symbol']:
768
- asyncio.create_task(realtime_monitor._monitor_single_trade(trade))
769
- break
770
- else:
771
- print("✅ Scan complete. No actionable opportunities identified.")
772
- else:
773
- print("😴 No available capital. Waiting for current trade to close.")
774
-
775
- finally:
776
- print("✅ Cycle finished. Releasing lock.")
777
- r2_service_global.release_lock()
778
- await r2_service_global.save_system_logs_async({"cycle_completed": True, "open_trades": len(open_trades)})
779
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
780
  except Exception as error:
781
  print(f"❌ Unhandled error in main cycle: {error}")
782
  traceback.print_exc()
783
- await r2_service_global.save_system_logs_async({"cycle_error": True, "error": str(error)})
784
- if r2_service_global.lock_acquired:
785
- r2_service_global.release_lock()
786
 
787
  @asynccontextmanager
788
  async def lifespan(application: FastAPI):
789
  global r2_service_global, data_manager_global, llm_service_global, learning_engine_global, realtime_monitor
790
  print("===== Application Startup =====")
791
-
792
  try:
793
  r2_service_global = R2Service()
794
  llm_service_global = LLMService()
795
  contracts_database = await r2_service_global.load_contracts_db_async()
796
 
 
797
  data_manager_global = DataManager(contracts_database)
798
  await data_manager_global.initialize()
799
 
800
- # ✅ تهيئة نظام التعلم مع تمرير data_manager
801
  learning_engine_global = LearningEngine(r2_service_global, data_manager_global)
802
- await learning_engine_global.initialize_enhanced() # ✅ استخدام التهيئة المحسنة
803
-
804
- # ✅ إجبار تحديث الاستراتيجيات من البيانات الحالية
805
  await learning_engine_global.force_strategy_learning()
806
 
807
- # ✅ التحقق من أن الأوزان يتم تحميلها
808
- if learning_engine_global.initialized:
809
- weights = await learning_engine_global.get_optimized_strategy_weights("bull_market")
810
- print(f"🎯 الأوزان المحملة: {weights}")
811
-
812
  realtime_monitor = RealTimeTradeMonitor()
813
 
 
814
  asyncio.create_task(monitor_market_async())
815
  asyncio.create_task(realtime_monitor.start_monitoring())
816
 
817
  await r2_service_global.save_system_logs_async({"application_started": True})
818
-
819
  print("\n✅ All services initialized. Application is ready.\n")
820
  yield
821
 
822
  except Exception as error:
823
  print(f"❌ Application startup failed: {error}")
824
  traceback.print_exc()
825
- if r2_service_global:
826
- await r2_service_global.save_system_logs_async({"application_startup_failed": True, "error": str(error)})
827
- raise
828
  finally:
 
829
  await cleanup_on_shutdown()
830
 
831
  application = FastAPI(lifespan=lifespan)
832
 
 
 
833
  @application.get("/run-cycle")
834
  async def run_cycle_api():
835
- """API endpoint to trigger the bot cycle."""
836
  asyncio.create_task(run_bot_cycle_async())
837
  return {"message": "Bot cycle initiated in the background."}
838
 
839
  @application.get("/health")
840
  async def health_check():
841
- """Detailed health check."""
842
- learning_metrics = {}
843
- if learning_engine_global and learning_engine_global.initialized:
844
- learning_metrics = await learning_engine_global.calculate_performance_metrics()
845
-
846
  return {
847
- "status": "healthy",
848
- "timestamp": datetime.now().isoformat(),
849
- "services": {
850
- "r2_service": "initialized" if r2_service_global else "uninitialized",
851
- "llm_service": "initialized" if llm_service_global else "uninitialized",
852
- "data_manager": "initialized" if data_manager_global else "uninitialized",
853
- "learning_engine": "active" if learning_engine_global and learning_engine_global.initialized else "inactive",
854
- "realtime_monitor": "running" if realtime_monitor and realtime_monitor.is_running else "stopped"
855
- },
856
  "market_state_ok": state.MARKET_STATE_OK,
857
- "learning_engine": learning_metrics
858
  }
859
 
860
- @application.get("/stats")
861
- async def get_performance_stats():
862
- """Get performance statistics for all services."""
863
- try:
864
- market_context = await data_manager_global.get_market_context_async() if data_manager_global else {}
865
-
866
- learning_stats = {}
867
- improvement_suggestions = []
868
- if learning_engine_global and learning_engine_global.initialized:
869
- learning_stats = await learning_engine_global.calculate_performance_metrics()
870
- improvement_suggestions = await learning_engine_global.suggest_improvements()
871
-
872
- stats = {
873
- "timestamp": datetime.now().isoformat(),
874
- "data_manager": data_manager_global.get_performance_stats() if data_manager_global else {},
875
- "market_state": {
876
- "is_healthy": state.MARKET_STATE_OK,
877
- "description": "Market is healthy for trading" if state.MARKET_STATE_OK else "Market conditions are unfavorable",
878
- "context": market_context
879
- },
880
- "realtime_monitoring": {
881
- "active_trades": len(realtime_monitor.monitoring_tasks) if realtime_monitor else 0,
882
- "is_running": realtime_monitor.is_running if realtime_monitor else False
883
- },
884
- "learning_engine": learning_stats,
885
- "improvement_suggestions": improvement_suggestions
886
- }
887
- return stats
888
- except Exception as error:
889
- raise HTTPException(status_code=500, detail=f"Failed to retrieve stats: {str(error)}")
890
-
891
- @application.get("/logs/status")
892
- async def get_logs_status():
893
- """Get status of logging system."""
894
- try:
895
- open_trades = await r2_service_global.get_open_trades_async()
896
- portfolio_state = await r2_service_global.get_portfolio_state_async()
897
-
898
- return {
899
- "logging_system": "active",
900
- "open_trades_count": len(open_trades),
901
- "current_capital": portfolio_state.get("current_capital_usd", 0),
902
- "total_trades": portfolio_state.get("total_trades", 0),
903
- "timestamp": datetime.now().isoformat()
904
- }
905
- except Exception as error:
906
- raise HTTPException(status_code=500, detail=f"Failed to get logs status: {str(error)}")
907
-
908
  async def cleanup_on_shutdown():
909
  """Cleanup function for graceful shutdown."""
910
- global r2_service_global, data_manager_global, realtime_monitor, learning_engine_global
911
  print("\n🛑 Shutdown signal received. Cleaning up...")
912
 
913
- if r2_service_global:
914
- try:
915
- await r2_service_global.save_system_logs_async({"application_shutdown": True})
916
- except Exception as log_error:
917
- print(f"⚠️ Failed to save shutdown log: {log_error}")
918
-
919
- if learning_engine_global and learning_engine_global.initialized:
920
- try:
921
- await learning_engine_global.save_weights_to_r2()
922
- await learning_engine_global.save_performance_history()
923
- print("✅ Learning engine data saved.")
924
- except Exception as e:
925
- print(f"⚠️ Failed to save learning engine data: {e}")
926
-
927
- if realtime_monitor:
928
- realtime_monitor.stop_monitoring()
929
 
930
- if data_manager_global:
931
- await data_manager_global.close()
932
- print("✅ Data manager connections closed.")
933
 
934
- if r2_service_global and r2_service_global.lock_acquired:
935
- r2_service_global.release_lock()
936
- print("✅ Lock released.")
 
 
 
 
 
 
 
937
 
938
  print("✅ Cleanup completed.")
939
 
940
  def signal_handler(signum, frame):
941
  """Handle shutdown signals."""
942
- print(f"\n⚠️ Received signal {signum}")
 
943
  asyncio.create_task(cleanup_on_shutdown())
 
 
944
  sys.exit(0)
945
 
946
  signal.signal(signal.SIGINT, signal_handler)
 
1
+ # app.py - الإصدار النهائي مع منطق إيقاف تداول معاير وإدارة محسنة للاتصالات
2
  import os
3
  import traceback
4
  import signal
 
19
  import re
20
 
21
  # إعدادات النظام
 
22
  OPPORTUNITY_COUNT = 10
 
23
 
24
  # المتغيرات العامة للنظام
25
  r2_service_global = None
 
33
  def __init__(self):
34
  self.monitoring_tasks = {}
35
  self.is_running = False
36
+
37
  async def start_monitoring(self):
 
38
  self.is_running = True
39
  print("🔍 Starting real-time trade monitoring...")
 
40
  while self.is_running:
41
  try:
42
  open_trades = await r2_service_global.get_open_trades_async()
 
43
  for trade in open_trades:
44
  symbol = trade['symbol']
45
  if symbol not in self.monitoring_tasks:
 
50
  for symbol in list(self.monitoring_tasks.keys()):
51
  if symbol not in current_symbols:
52
  del self.monitoring_tasks[symbol]
 
53
  await asyncio.sleep(10)
 
54
  except Exception as error:
55
  print(f"❌ Real-time monitor error: {error}")
56
  await asyncio.sleep(30)
57
+
58
  async def _monitor_single_trade(self, trade):
 
59
  symbol = trade['symbol']
60
  strategy = trade.get('strategy', 'GENERIC')
61
  print(f"📊 Starting real-time monitoring for {symbol} (Strategy: {strategy})")
 
62
  while symbol in self.monitoring_tasks and self.is_running:
63
  try:
64
  current_price = await data_manager_global.get_latest_price_async(symbol)
 
74
  close_reason = ""
75
 
76
  if stop_loss and current_price <= stop_loss:
77
+ should_close, close_reason = True, f"Stop loss hit: {current_price} <= {stop_loss}"
 
78
  elif take_profit and current_price >= take_profit:
79
+ should_close, close_reason = True, f"Take profit hit: {current_price} >= {take_profit}"
80
+
 
 
 
 
 
 
 
81
  if should_close:
82
+ print(f"🚨 IMMEDIATE CLOSE: {symbol} - {close_reason}")
 
83
  if r2_service_global.acquire_lock():
84
  try:
85
  await r2_service_global.close_trade_async(trade, current_price)
86
+ print(f"✅ Trade {symbol} closed immediately at {current_price}.")
 
87
  if learning_engine_global and learning_engine_global.initialized:
88
  await learning_engine_global.analyze_trade_outcome(trade, 'CLOSED_BY_MONITOR')
 
89
  asyncio.create_task(run_bot_cycle_async())
 
90
  finally:
91
  r2_service_global.release_lock()
92
+ if symbol in self.monitoring_tasks: del self.monitoring_tasks[symbol]
 
 
93
  break
 
94
  await asyncio.sleep(15)
 
95
  except Exception as error:
96
  print(f"❌ Real-time monitoring error for {symbol}: {error}")
97
  await asyncio.sleep(30)
98
+
99
  def stop_monitoring(self):
 
100
  self.is_running = False
101
  self.monitoring_tasks.clear()
102
+ print("🛑 Real-time trade monitoring stopped.")
103
 
104
  async def monitor_market_async():
105
+ """Background task to continuously monitor market health with calibrated logic."""
106
+ while data_manager_global is None:
 
 
 
 
107
  await asyncio.sleep(3)
108
+
 
 
 
 
 
109
  while True:
110
  try:
111
  print("👁️ Monitoring market sentiment...")
112
+ market_context = await data_manager_global.get_market_context_async()
 
 
 
 
 
 
113
  if not market_context:
114
+ print("⚠️ Could not get market context. Assuming neutral state.")
115
  state.MARKET_STATE_OK = True
116
  await asyncio.sleep(60)
117
  continue
118
 
119
  whale_analysis = market_context.get('general_whale_activity', {})
 
120
  is_critical = whale_analysis.get('critical_alert', False)
 
 
 
 
 
121
  bitcoin_sentiment = market_context.get('btc_sentiment')
122
  fear_greed_index = market_context.get('fear_and_greed_index')
123
 
124
+ # ✅ ## منطق إيقاف التداول المُعاير والذكي ##
125
+ # لن يتم الإيقاف إلا إذا اجتمعت عدة إشارات سلبية قوية.
126
  should_halt_trading = False
127
  halt_reason = ""
128
 
129
+ # بناء قائمة الأسباب
130
+ reasons = []
131
+ if is_critical: reasons.append("CRITICAL whale activity")
132
+ if fear_greed_index is not None and fear_greed_index < 30: reasons.append(f"Extreme Fear (F&G: {fear_greed_index})")
133
+ if bitcoin_sentiment == 'BEARISH': reasons.append("Bearish BTC sentiment")
134
+
135
+ # القرار: التوقف فقط إذا كان هناك سببان قويان أو أكثر
136
+ if len(reasons) >= 2:
137
  should_halt_trading = True
138
+ halt_reason = "High-risk market conditions detected: " + " AND ".join(reasons)
139
 
140
  if should_halt_trading:
141
+ if state.MARKET_STATE_OK: # فقط اطبع الرسالة مرة واحدة عند التغيير
142
+ print(f"🚨🚨🚨 MARKET HALT: {halt_reason} 🚨🚨🚨")
143
+ await r2_service_global.save_system_logs_async({"market_halt": True, "reason": halt_reason})
144
  state.MARKET_STATE_OK = False
 
 
 
 
 
 
 
 
 
 
145
  else:
146
  if not state.MARKET_STATE_OK:
147
+ print("🟢 Market conditions have improved. Resuming normal operations.")
148
  state.MARKET_STATE_OK = True
149
 
150
+ await asyncio.sleep(60) # فحص كل دقيقة
151
  except Exception as error:
152
+ print(f"❌ An error occurred during market monitoring: {error}")
153
  traceback.print_exc()
154
+ state.MARKET_STATE_OK = True # افتراض الحالة الآمنة عند حدوث خطأ
155
  await asyncio.sleep(60)
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  async def find_new_opportunities_async():
158
+ """Finds new trading opportunities using the 3-stage filtering system."""
159
+ print("🔍 Scanning for new opportunities...")
160
  try:
 
 
 
 
 
161
  market_context = await data_manager_global.get_market_context_async()
162
  if not market_context:
163
+ print("❌ Failed to fetch market context. Aborting scan.")
164
+ return None
165
 
166
+ # استخدام نظام الفلترة السريع والفعال
167
+ candidates = await data_manager_global.fast_snapshot_filter(150)
168
+ lightweight_candidates = await data_manager_global.lightweight_indicator_filter(candidates)
 
 
 
 
 
 
 
 
 
169
 
170
+ if not lightweight_candidates:
171
+ print(" No promising candidates found after initial filtering.")
172
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
+ print(f"🧠 Deep analyzing top {OPPORTUNITY_COUNT} candidates...")
175
+ # التحليل العميق لأفضل المرشحين فقط
176
+ ohlcv_data_list = await data_manager_global.get_fast_pass_data_async(lightweight_candidates[:OPPORTUNITY_COUNT])
177
+
178
+ feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
179
+ processed_candidates = []
180
+ for raw_data in ohlcv_data_list:
181
+ processed = await feature_processor.process_and_score_symbol_enhanced(raw_data)
182
+ if processed:
183
+ processed_candidates.append(processed)
184
 
185
+ top_candidates = feature_processor.filter_top_candidates(processed_candidates, OPPORTUNITY_COUNT)
186
+
187
  if not top_candidates:
188
+ print(" No strong candidates left after final scoring.")
189
+ return None
 
 
 
 
190
 
191
+ print(f"🧠 Getting LLM analysis for top {len(top_candidates)} candidates...")
 
192
  for candidate in top_candidates:
193
  try:
194
+ llm_analysis = await llm_service_global.get_trading_decision(candidate)
195
+ if llm_analysis and llm_analysis.get('action') == "BUY":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  print("\n========================================================")
197
  print(f"💎💎💎 New Trading Opportunity Identified! 💎💎💎")
198
  print(f" Symbol: {candidate['symbol']}")
199
+ print(f" Strategy: {llm_analysis.get('strategy', 'N/A')}")
200
+ print(f" Reasoning: {llm_analysis.get('reasoning')}")
 
 
201
  print("========================================================\n")
202
+ await r2_service_global.save_system_logs_async({"new_opportunity_found": True, "symbol": candidate['symbol'], "strategy": llm_analysis.get('strategy')})
203
+ return {"symbol": candidate['symbol'], "decision": llm_analysis, "current_price": candidate['current_price']}
204
+ else:
205
+ print(f"🧠 LLM decided to HOLD on {candidate.get('symbol', 'unknown')}.")
206
+ except Exception as e:
207
+ print(f"❌ LLM analysis failed for {candidate.get('symbol', 'unknown')}: {e}")
 
 
 
 
 
 
 
 
 
 
 
208
 
209
+ print("✅ Cycle finished. No actionable BUY opportunities found by LLM.")
210
  return None
 
211
  except Exception as error:
212
  print(f"❌ An error occurred while scanning for opportunities: {error}")
213
  traceback.print_exc()
 
214
  return None
215
 
216
  async def re_analyze_open_trade_async(trade_data):
217
+ """Re-analyzes an open trade."""
218
  symbol = trade_data.get('symbol')
219
+ print(f"⏳ Re-analyzing trade: {symbol}")
220
  try:
221
+ market_context = await data_manager_global.get_market_context_async()
222
+ symbol_data = await data_manager_global.get_fast_pass_data_async([{'symbol': symbol, 'reasons': ['re-analysis']}])
223
+ if not symbol_data:
224
+ print(f"❌ Failed to fetch latest data for {symbol} re-analysis.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  return None
226
+
 
 
 
 
 
 
 
 
 
227
  feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
228
+ processed_data = await feature_processor.process_and_score_symbol(symbol_data[0])
 
229
  if not processed_data:
230
+ print(f"❌ Failed to process latest data for {symbol} re-analysis.")
231
  return None
232
 
233
+ re_analysis = await llm_service_global.re_analyze_trade_async(trade_data, processed_data)
234
+ print(f"✅ Re-analysis for {symbol} suggests: {re_analysis.get('action')}")
235
+ return {"symbol": symbol, "decision": re_analysis, "current_price": processed_data.get('current_price')}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  except Exception as error:
237
+ print(f"❌ An error occurred during trade re-analysis for {symbol}: {error}")
238
  traceback.print_exc()
 
239
  return None
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  async def run_bot_cycle_async():
242
+ """The main asynchronous bot cycle."""
243
+ print(f"\n{'='*50}\n⏳ New cycle initiated at: {datetime.now().isoformat()}\n{'='*50}")
 
 
244
 
245
+ if not state.MARKET_STATE_OK:
246
+ print("Market is currently halted. Skipping trading cycle.")
247
+ return
 
 
 
248
 
249
+ if not r2_service_global.acquire_lock():
250
+ print("❌ Failed to acquire lock. Another cycle is already running. Skipping.")
251
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
+ try:
254
+ await r2_service_global.save_system_logs_async({"cycle_started": True})
255
+ open_trades = await r2_service_global.get_open_trades_async()
256
+ should_look_for_new_trade = not open_trades
257
+
258
+ if open_trades:
259
+ trade = open_trades[0] # Assuming one trade at a time for now
260
+ result = await re_analyze_open_trade_async(trade)
261
+ if result and result['decision'].get('action') == "CLOSE_TRADE":
262
+ await r2_service_global.close_trade_async(trade, result['current_price'])
263
+ print(f"✅ Trade for {trade['symbol']} CLOSED based on re-analysis.")
264
+ if learning_engine_global:
265
+ await learning_engine_global.analyze_trade_outcome(trade, 'CLOSED_BY_REANALYSIS')
266
+ should_look_for_new_trade = True
267
+ else:
268
+ print(f"✅ Trade for {trade['symbol']} remains on HOLD.")
269
+
270
+ if should_look_for_new_trade:
271
+ portfolio_state = await r2_service_global.get_portfolio_state_async()
272
+ if portfolio_state.get("current_capital_usd", 0) > 1:
273
+ new_opportunity = await find_new_opportunities_async()
274
+ if new_opportunity:
275
+ await r2_service_global.save_new_trade_async(
276
+ new_opportunity['symbol'], new_opportunity['decision'], new_opportunity['current_price']
277
+ )
278
+ else:
279
+ print("😴 No available capital. Waiting for current trade to close.")
280
+
281
  except Exception as error:
282
  print(f"❌ Unhandled error in main cycle: {error}")
283
  traceback.print_exc()
284
+ finally:
285
+ print("✅ Cycle finished. Releasing lock.")
286
+ r2_service_global.release_lock()
287
 
288
  @asynccontextmanager
289
  async def lifespan(application: FastAPI):
290
  global r2_service_global, data_manager_global, llm_service_global, learning_engine_global, realtime_monitor
291
  print("===== Application Startup =====")
 
292
  try:
293
  r2_service_global = R2Service()
294
  llm_service_global = LLMService()
295
  contracts_database = await r2_service_global.load_contracts_db_async()
296
 
297
+ # ✅ تهيئة مدير البيانات الذي يدير الآن الاتصالات بشكل مركزي
298
  data_manager_global = DataManager(contracts_database)
299
  await data_manager_global.initialize()
300
 
 
301
  learning_engine_global = LearningEngine(r2_service_global, data_manager_global)
302
+ await learning_engine_global.initialize_enhanced()
 
 
303
  await learning_engine_global.force_strategy_learning()
304
 
 
 
 
 
 
305
  realtime_monitor = RealTimeTradeMonitor()
306
 
307
+ # بدء المهام الخلفية
308
  asyncio.create_task(monitor_market_async())
309
  asyncio.create_task(realtime_monitor.start_monitoring())
310
 
311
  await r2_service_global.save_system_logs_async({"application_started": True})
 
312
  print("\n✅ All services initialized. Application is ready.\n")
313
  yield
314
 
315
  except Exception as error:
316
  print(f"❌ Application startup failed: {error}")
317
  traceback.print_exc()
 
 
 
318
  finally:
319
+ # 🔒 إغلاق آمن للموارد عند إيقاف التشغيل
320
  await cleanup_on_shutdown()
321
 
322
  application = FastAPI(lifespan=lifespan)
323
 
324
+ # ... (API endpoints like /run-cycle, /health, etc., remain unchanged) ...
325
+
326
  @application.get("/run-cycle")
327
  async def run_cycle_api():
 
328
  asyncio.create_task(run_bot_cycle_async())
329
  return {"message": "Bot cycle initiated in the background."}
330
 
331
  @application.get("/health")
332
  async def health_check():
 
 
 
 
 
333
  return {
334
+ "status": "healthy", "timestamp": datetime.now().isoformat(),
 
 
 
 
 
 
 
 
335
  "market_state_ok": state.MARKET_STATE_OK,
336
+ "lock_acquired": r2_service_global.lock_acquired if r2_service_global else False
337
  }
338
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  async def cleanup_on_shutdown():
340
  """Cleanup function for graceful shutdown."""
 
341
  print("\n🛑 Shutdown signal received. Cleaning up...")
342
 
343
+ if realtime_monitor: realtime_monitor.stop_monitoring()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
 
345
+ # ✅ إغلاق مدير البيانات سيقوم بإغلاق جميع الاتصالات المدارة مركزيًا
346
+ if data_manager_global: await data_manager_global.close()
 
347
 
348
+ if learning_engine_global:
349
+ await learning_engine_global.save_weights_to_r2()
350
+ await learning_engine_global.save_performance_history()
351
+ print("✅ Learning engine data saved.")
352
+
353
+ if r2_service_global:
354
+ await r2_service_global.save_system_logs_async({"application_shutdown": True})
355
+ if r2_service_global.lock_acquired:
356
+ r2_service_global.release_lock()
357
+ print("✅ Lock released.")
358
 
359
  print("✅ Cleanup completed.")
360
 
361
  def signal_handler(signum, frame):
362
  """Handle shutdown signals."""
363
+ print(f"\n⚠️ Received signal {signum}, initiating graceful shutdown.")
364
+ # Use a task to ensure cleanup can complete
365
  asyncio.create_task(cleanup_on_shutdown())
366
+ # Give it a moment to start before exiting
367
+ time.sleep(2)
368
  sys.exit(0)
369
 
370
  signal.signal(signal.SIGINT, signal_handler)