Riy777 commited on
Commit
35b000d
·
1 Parent(s): da901ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -330
app.py CHANGED
@@ -1,34 +1,22 @@
1
- import os
2
- import traceback
3
- import signal
4
- import sys
5
- import uvicorn
6
- import asyncio
7
  from contextlib import asynccontextmanager
8
- from fastapi import FastAPI, BackgroundTasks, HTTPException
9
- from datetime import datetime, timedelta
10
  from r2 import R2Service
11
  from LLM import LLMService, local_analyze_opportunity, local_re_analyze_trade
12
  from data_manager import DataManager
13
  from ML import MLProcessor as FeatureProcessor
14
  from learning_engine import LearningEngine
15
- import time
16
- import json
17
  import state
18
- import re
19
-
20
- from whale_news_data import whale_monitor_global
21
  from helpers import safe_float_conversion, _apply_patience_logic
22
 
23
- TOP_N_SYMBOLS = 100
24
- OPPORTUNITY_COUNT = 10
25
- CHUNK_SIZE = 5
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
  class RealTimeTradeMonitor:
34
  def __init__(self):
@@ -37,7 +25,6 @@ class RealTimeTradeMonitor:
37
 
38
  async def start_monitoring(self):
39
  self.is_running = True
40
- print("🔍 Starting real-time trade monitoring...")
41
 
42
  while self.is_running:
43
  try:
@@ -57,13 +44,11 @@ class RealTimeTradeMonitor:
57
  await asyncio.sleep(10)
58
 
59
  except Exception as error:
60
- print(f"Real-time monitor error: {error}")
61
  await asyncio.sleep(30)
62
 
63
  async def _monitor_single_trade(self, trade):
64
  symbol = trade['symbol']
65
- strategy = trade.get('strategy', 'GENERIC')
66
- print(f"📊 Starting real-time monitoring for {symbol} (Strategy: {strategy})")
67
 
68
  while symbol in self.monitoring_tasks and self.is_running:
69
  try:
@@ -90,15 +75,11 @@ class RealTimeTradeMonitor:
90
  dynamic_stop = current_price * 0.98
91
  if dynamic_stop > (stop_loss or 0):
92
  trade['stop_loss'] = dynamic_stop
93
- print(f"🔒 Updated trailing stop for {symbol}: {dynamic_stop:.4f}")
94
 
95
  if should_close:
96
- print(f"🚨 IMMEDIATE CLOSE: {symbol} - {close_reason} - Strategy: {strategy}")
97
-
98
  if r2_service_global.acquire_lock():
99
  try:
100
  await r2_service_global.close_trade_async(trade, current_price)
101
- print(f"✅ Trade {symbol} closed immediately at {current_price}. Strategy: {strategy}")
102
 
103
  if learning_engine_global and learning_engine_global.initialized:
104
  await learning_engine_global.analyze_trade_outcome(trade, 'CLOSED_BY_MONITOR')
@@ -115,56 +96,35 @@ class RealTimeTradeMonitor:
115
  await asyncio.sleep(15)
116
 
117
  except Exception as error:
118
- print(f"Real-time monitoring error for {symbol}: {error}")
119
  await asyncio.sleep(30)
120
 
121
  def stop_monitoring(self):
122
  self.is_running = False
123
  self.monitoring_tasks.clear()
124
- print("🛑 Real-time trade monitoring stopped")
125
 
126
  async def monitor_market_async():
127
- global data_manager_global
128
 
129
  init_attempts = 0
130
  while data_manager_global is None and init_attempts < 10:
131
- print(f"⏳ Waiting for data manager initialization... (attempt {init_attempts + 1}/10)")
132
  await asyncio.sleep(3)
133
  init_attempts += 1
134
 
135
  if data_manager_global is None:
136
- print("❌ Data manager failed to initialize after 10 attempts")
137
  return
138
 
139
  while True:
140
  try:
141
- print("👁️ Monitoring market sentiment...")
142
-
143
- try:
144
- market_context = await data_manager_global.get_market_context_async()
145
- except Exception as error:
146
- print(f"⚠️ Failed to get market context: {error}")
147
- market_context = await get_fallback_market_context()
148
 
149
  if not market_context:
150
- print("❌ Failed to get market context. Assuming neutral state.")
151
  state.MARKET_STATE_OK = True
152
  await asyncio.sleep(60)
153
  continue
154
 
155
  whale_analysis = market_context.get('general_whale_activity', {})
156
- whale_sentiment = whale_analysis.get('sentiment', 'NEUTRAL')
157
  is_critical = whale_analysis.get('critical_alert', False)
158
- total_volume = whale_analysis.get('total_volume_usd', 0)
159
-
160
- netflow_analysis = whale_analysis.get('netflow_analysis', {})
161
- net_flow = netflow_analysis.get('net_flow', 0)
162
- flow_direction = netflow_analysis.get('flow_direction', 'BALANCED')
163
- market_impact = netflow_analysis.get('market_impact', 'UNKNOWN')
164
-
165
- print(f"🐋 Whale Analysis: {whale_sentiment} | Critical: {is_critical} | Volume: ${total_volume:,.0f}")
166
- print(f"📈 Net Flow: ${net_flow:,.0f} ({flow_direction}) | Market Impact: {market_impact}")
167
- print(f"📊 Whale Description: {whale_analysis.get('description', 'No data')}")
168
 
169
  bitcoin_sentiment = market_context.get('btc_sentiment')
170
  fear_greed_index = market_context.get('fear_and_greed_index')
@@ -174,60 +134,28 @@ async def monitor_market_async():
174
 
175
  if is_critical:
176
  should_halt_trading = True
177
- halt_reason = f"CRITICAL whale activity detected: {whale_analysis.get('description')}"
178
  elif bitcoin_sentiment == 'BEARISH' and (fear_greed_index is not None and fear_greed_index < 30):
179
  should_halt_trading = True
180
- halt_reason = f"Bearish market conditions (BTC: {bitcoin_sentiment}, F&G: {fear_greed_index})"
181
- elif netflow_analysis and net_flow < -1000000 and market_impact == 'HIGH':
182
- should_halt_trading = True
183
- halt_reason = f"Strong sell pressure detected: ${abs(net_flow):,.0f} net outflow to exchanges"
184
 
185
  if should_halt_trading:
186
- print(f"🚨🚨🚨 MARKET HALT: {halt_reason} 🚨🚨🚨")
187
  state.MARKET_STATE_OK = False
188
-
189
- try:
190
- await r2_service_global.save_system_logs_async({
191
- "market_halt": True,
192
- "reason": halt_reason,
193
- "whale_sentiment": whale_sentiment,
194
- "is_critical": is_critical,
195
- "net_flow": net_flow,
196
- "flow_direction": flow_direction
197
- })
198
- except Exception as log_error:
199
- print(f"⚠️ Failed to save market halt log: {log_error}")
200
  else:
201
  if not state.MARKET_STATE_OK:
202
- print("🟢 Market conditions improved. Resuming normal operations.")
203
  state.MARKET_STATE_OK = True
204
 
205
  await asyncio.sleep(60)
206
  except Exception as error:
207
- print(f" An error occurred during COMPREHENSIVE market monitoring: {error}")
208
- traceback.print_exc()
209
  state.MARKET_STATE_OK = True
210
  await asyncio.sleep(60)
211
 
212
- async def get_fallback_market_context():
213
- return {
214
- 'timestamp': datetime.now().isoformat(),
215
- 'general_whale_activity': {
216
- 'sentiment': 'NEUTRAL',
217
- 'description': 'Fallback mode - system initializing',
218
- 'critical_alert': False,
219
- 'transaction_count': 0,
220
- 'total_volume_usd': 0,
221
- 'netflow_analysis': {
222
- 'net_flow': 0,
223
- 'flow_direction': 'BALANCED',
224
- 'market_impact': 'LOW'
225
- }
226
- },
227
- 'btc_sentiment': 'NEUTRAL',
228
- 'fear_and_greed_index': 50
229
- }
230
-
231
  async def validate_candidate_data_enhanced(candidate):
232
  try:
233
  required_fields = ['symbol', 'current_price', 'final_score', 'enhanced_final_score']
@@ -240,10 +168,10 @@ async def validate_candidate_data_enhanced(candidate):
240
  candidate['final_score'] = safe_float_conversion(candidate.get('final_score'), 0.5)
241
  candidate['enhanced_final_score'] = safe_float_conversion(candidate.get('enhanced_final_score'), candidate['final_score'])
242
 
243
- if 'reasons_for_candidacy' not in candidate or not candidate['reasons_for_candidacy']:
244
  candidate['reasons_for_candidacy'] = ['unknown_reason']
245
 
246
- if 'sentiment_data' not in candidate or not candidate['sentiment_data']:
247
  candidate['sentiment_data'] = {
248
  'btc_sentiment': 'NEUTRAL',
249
  'fear_and_greed_index': 50,
@@ -256,61 +184,34 @@ async def validate_candidate_data_enhanced(candidate):
256
  if 'strategy_scores' not in candidate:
257
  candidate['strategy_scores'] = {}
258
 
259
- if 'recommended_strategy' not in candidate:
260
- candidate['recommended_strategy'] = 'unknown'
261
-
262
- if 'target_strategy' not in candidate or not candidate['target_strategy'] or candidate['target_strategy'] == 'unknown':
263
  candidate['target_strategy'] = 'GENERIC'
264
 
265
  return True
266
 
267
  except Exception as error:
268
- print(f"Failed to validate candidate data for {candidate.get('symbol')}: {error}")
269
  return False
270
 
271
  async def analyze_market_strategy(market_context):
272
  try:
273
  whale_analysis = market_context.get('general_whale_activity', {})
274
  netflow_analysis = whale_analysis.get('netflow_analysis', {})
275
- trading_signals = whale_analysis.get('trading_signals', [])
276
 
277
  prompt = f"""
278
- You are a professional crypto portfolio manager. Analyze the current market conditions and determine the most suitable strategy.
279
 
280
- **Enhanced Market Data:**
281
- - BTC Price: {market_context.get('bitcoin_price_usd')}
282
  - BTC Sentiment: {market_context.get('btc_sentiment')}
283
  - Fear & Greed Index: {market_context.get('fear_and_greed_index')}
284
  - Whale Analysis: {whale_analysis.get('sentiment')}
285
- - Critical Whale Alert: {whale_analysis.get('critical_alert')}
286
- - Net Flow Analysis: ${netflow_analysis.get('net_flow', 0):,.0f} ({netflow_analysis.get('flow_direction', 'BALANCED')})
287
- - Market Impact: {netflow_analysis.get('market_impact', 'UNKNOWN')}
288
- - Active Trading Signals: {len(trading_signals)}
289
-
290
- **Available Strategies:**
291
- 1. AGGRESSIVE_GROWTH - For strong bull markets with positive net flow.
292
- 2. DEFENSIVE_GROWTH - For volatile or uncertain markets.
293
- 3. CONSERVATIVE - For bearish or high-risk markets with negative net flow.
294
- 4. HIGH_FREQUENCY - For sideways markets with balanced flow.
295
- 5. WHALE_FOLLOWING - When whale activity is high and clear signals present.
296
- 6. GENERIC - Balanced approach for normal conditions.
297
-
298
- **Enhanced Decision Factors:**
299
- - Net Flow > $1M positive: Consider AGGRESSIVE_GROWTH
300
- - Net Flow < $1M negative: Consider CONSERVATIVE
301
- - Strong whale signals: Consider WHALE_FOLLOWING
302
- - Critical alerts: Use CONSERVATIVE regardless of other factors
303
-
304
- **Required:**
305
- - Choose one primary strategy.
306
- - Explain why in a single sentence.
307
- - Set an acceptable risk tolerance (1 to 10).
308
- - Determine the optimal number of coins to scan (50 to 200).
309
-
310
- **Output (JSON only):**
311
  {{
312
  "primary_strategy": "STRATEGY_NAME",
313
- "reasoning": "Brief reasoning including net flow analysis.",
314
  "risk_tolerance": 5,
315
  "optimal_scan_count": 100
316
  }}
@@ -319,26 +220,23 @@ async def analyze_market_strategy(market_context):
319
  response = await llm_service_global._call_llm(prompt)
320
 
321
  try:
322
- json_match = re.search(r'\{.*\}', response, re.DOTALL)
323
- strategy_data = json.loads(json_match.group())
 
324
  except:
325
  net_flow = netflow_analysis.get('net_flow', 0)
326
  if net_flow > 1000000:
327
  fallback_strategy = "AGGRESSIVE_GROWTH"
328
- reasoning = "Strong positive net flow detected"
329
  elif net_flow < -1000000:
330
  fallback_strategy = "CONSERVATIVE"
331
- reasoning = "Strong negative net flow detected"
332
  elif whale_analysis.get('critical_alert'):
333
  fallback_strategy = "CONSERVATIVE"
334
- reasoning = "Critical whale alert active"
335
  else:
336
  fallback_strategy = "GENERIC"
337
- reasoning = "Balanced market conditions"
338
 
339
  strategy_data = {
340
  "primary_strategy": fallback_strategy,
341
- "reasoning": f"Fallback: {reasoning}",
342
  "risk_tolerance": 5,
343
  "optimal_scan_count": 100,
344
  }
@@ -346,7 +244,7 @@ async def analyze_market_strategy(market_context):
346
  return strategy_data
347
 
348
  except Exception as error:
349
- print(f"Failed to analyze market strategy: {error}")
350
  return {
351
  "primary_strategy": "GENERIC",
352
  "reasoning": "Fallback due to analysis error",
@@ -359,12 +257,10 @@ async def find_strategy_specific_candidates(strategy, scan_count):
359
  all_candidates = await data_manager_global.find_high_potential_candidates(scan_count * 2)
360
 
361
  if not all_candidates:
362
- print(f"⚠️ الماسح العام لم يجد أي مرشحين أوليين.")
363
  return []
364
 
365
  market_context = await data_manager_global.get_market_context_async()
366
  if not market_context:
367
- print("❌ Failed to get market context for strategy analysis")
368
  return []
369
 
370
  feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
@@ -376,21 +272,13 @@ async def find_strategy_specific_candidates(strategy, scan_count):
376
  ohlcv_data = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
377
 
378
  if ohlcv_data and ohlcv_data[0]:
379
- try:
380
- updated_market_context = await data_manager_global.get_market_context_async()
381
- if updated_market_context:
382
- feature_processor.market_context = updated_market_context
383
- except Exception as e:
384
- print(f"⚠️ Failed to update market context for {candidate['symbol']}: {e}")
385
-
386
  processed = await feature_processor.process_and_score_symbol_enhanced(ohlcv_data[0])
387
  if processed:
388
  processed_candidates.append(processed)
389
  except Exception as e:
390
- print(f"⚠️ فشل معالجة {candidate.get('symbol')}: {e}")
391
 
392
  if not processed_candidates:
393
- print("⚠️ لم يتم معالجة أي مرشح بنجاح")
394
  return []
395
 
396
  if strategy != 'GENERIC':
@@ -402,76 +290,55 @@ async def find_strategy_specific_candidates(strategy, scan_count):
402
  if strategy_score > 0.2:
403
  candidate['strategy_match_score'] = strategy_score
404
  strategy_candidates.append(candidate)
405
- print(f"✅ {candidate['symbol']} مناسب لـ {strategy} (درجة: {strategy_score:.3f})")
406
 
407
  sorted_candidates = sorted(strategy_candidates,
408
  key=lambda x: x.get('strategy_match_score', 0),
409
  reverse=True)
410
  top_candidates = sorted_candidates[:15]
411
-
412
- print(f"✅ تم اختيار {len(top_candidates)} مرشحًا لاستراتيجية {strategy}")
413
  else:
414
  sorted_candidates = sorted(processed_candidates,
415
  key=lambda x: x.get('enhanced_final_score', 0),
416
  reverse=True)
417
  top_candidates = sorted_candidates[:15]
418
- print(f"✅ تم اختيار {len(top_candidates)} مرشحًا للاستراتيجية العامة")
419
 
420
  return top_candidates
421
 
422
  except Exception as error:
423
- print(f" فشل في نظام الفلترة المتقدم: {error}")
424
- traceback.print_exc()
425
  return []
426
 
427
  async def find_new_opportunities_async():
428
- print("🔍 Scanning for new opportunities with enhanced data analysis...")
429
  try:
430
  await r2_service_global.save_system_logs_async({
431
- "opportunity_scan_started": True, "timestamp": datetime.now().isoformat()
432
  })
433
 
434
- print("🧠 Determining trading strategy with enhanced market analysis...")
435
  market_context = await data_manager_global.get_market_context_async()
436
  if not market_context:
437
- print("❌ Failed to fetch market context. Cannot determine strategy.")
438
  return
439
 
440
  strategy_decision = await analyze_market_strategy(market_context)
441
 
442
- print(f"🎯 Selected Strategy: {strategy_decision['primary_strategy']}")
443
- print(f"📝 Reasoning: {strategy_decision['reasoning']}")
444
- print(f"⚡ Risk Tolerance: {strategy_decision.get('risk_tolerance', 5)}/10")
445
- print(f"🔍 Optimal Scan Count: {strategy_decision.get('optimal_scan_count', 100)}")
446
-
447
- print(f"🔍 Finding top candidates using enhanced dynamic ranking...")
448
  high_potential_candidates = await find_strategy_specific_candidates(
449
  strategy_decision['primary_strategy'],
450
  strategy_decision.get('optimal_scan_count', 100)
451
  )
452
 
453
  if not high_potential_candidates:
454
- print("🔄 لا توجد مرشحين متخصصين، جلب مرشحين عامين...")
455
  high_potential_candidates = await data_manager_global.find_high_potential_candidates(20)
456
  if high_potential_candidates:
457
  for candidate in high_potential_candidates:
458
  candidate['target_strategy'] = 'GENERIC'
459
- print(f"✅ تم تحميل {len(high_potential_candidates)} مر��ح عام")
460
  else:
461
- print("✅ No new candidates found after dynamic ranking.")
462
- await r2_service_global.save_system_logs_async({
463
- "no_candidates_found": True, "strategy": strategy_decision['primary_strategy'],
464
- "reason": "Scanner did not return any initial candidates."
465
- })
466
  return
467
 
468
  all_processed_candidates = []
 
 
469
  for index in range(0, len(high_potential_candidates), CHUNK_SIZE):
470
  chunk = high_potential_candidates[index:index+CHUNK_SIZE]
471
-
472
  chunk_data = await data_manager_global.get_fast_pass_data_async(chunk)
473
 
474
- print(f"⏳ Processing and scoring chunk {index//CHUNK_SIZE + 1}...")
475
  updated_market_context = await data_manager_global.get_market_context_async()
476
  if not updated_market_context:
477
  updated_market_context = market_context
@@ -486,7 +353,6 @@ async def find_new_opportunities_async():
486
  await asyncio.sleep(1)
487
 
488
  if not all_processed_candidates:
489
- print("❌ No candidates were processed successfully.")
490
  return
491
 
492
  updated_market_context = await data_manager_global.get_market_context_async()
@@ -494,74 +360,46 @@ async def find_new_opportunities_async():
494
  updated_market_context = market_context
495
 
496
  feature_processor = FeatureProcessor(updated_market_context, data_manager_global, learning_engine_global)
 
497
  top_candidates = feature_processor.filter_top_candidates(all_processed_candidates, OPPORTUNITY_COUNT)
498
 
499
- print(f"✅ Identified {len(top_candidates)} top candidates after final scoring.")
500
-
501
  await r2_service_global.save_candidates_data_async(
502
  candidates_data=top_candidates,
503
  reanalysis_data={
504
  "strategy_used": strategy_decision,
505
- "market_conditions": market_context,
506
- "enhanced_analysis": True
507
  }
508
  )
509
 
510
  if not top_candidates:
511
- print("❌ No strong candidates left after final filtering.")
512
- await r2_service_global.save_system_logs_async({
513
- "no_strong_candidates": True,
514
- "strategy": strategy_decision['primary_strategy'],
515
- "initial_candidates_count": len(high_potential_candidates),
516
- "enhanced_analysis": True
517
- })
518
  return
519
 
520
- print("🧠 Getting LLM analysis for top candidates with enhanced data...")
521
-
522
  for candidate in top_candidates:
523
  try:
524
  if not await validate_candidate_data_enhanced(candidate):
525
- print(f"⚠️ Skipping {candidate.get('symbol')} due to quality issues")
526
  continue
527
 
528
  llm_analysis_data = await llm_service_global.get_trading_decision(candidate)
529
 
530
  if not llm_analysis_data:
531
- print(f"⚠️ LLM analysis failed for {candidate['symbol']}. Moving to next.")
532
  continue
533
 
534
  if llm_analysis_data.get('action') == "HOLD":
535
- print(f"🧠 LLM decided to HOLD on {candidate['symbol']}. Moving to next.")
536
  continue
537
 
538
  if llm_analysis_data.get('action') in ["BUY", "SELL"]:
539
  final_strategy = llm_analysis_data.get('strategy')
540
  candidate_strategy = candidate.get('target_strategy', 'GENERIC')
541
 
542
- if not final_strategy or final_strategy == 'unknown' or final_strategy == 'GENERIC':
543
  final_strategy = candidate_strategy
544
  llm_analysis_data['strategy'] = final_strategy
545
- print(f"🔧 تصحيح استراتيجية LLM لـ {candidate['symbol']}: {final_strategy}")
546
-
547
- print(f"🎯 الاستراتيجية النهائية: {final_strategy}")
548
-
549
- print("\n========================================================")
550
- print(f"💎💎💎 New Trading Opportunity Identified! 💎💎💎")
551
- print(f" Symbol: {candidate['symbol']}")
552
- print(f" Action: {llm_analysis_data.get('action')}")
553
- print(f" Strategy: {final_strategy}")
554
- print(f" Reasoning: {llm_analysis_data.get('reasoning')}")
555
- print(f" Confidence: {llm_analysis_data.get('confidence_level')}")
556
- print("========================================================\n")
557
 
558
  await r2_service_global.save_system_logs_async({
559
  "new_opportunity_found": True,
560
  "symbol": candidate['symbol'],
561
  "action": llm_analysis_data.get('action'),
562
- "strategy": final_strategy,
563
- "confidence": llm_analysis_data.get('confidence_level', 0),
564
- "enhanced_analysis": True
565
  })
566
 
567
  return {
@@ -572,19 +410,15 @@ async def find_new_opportunities_async():
572
  }
573
 
574
  except Exception as error:
575
- print(f"LLM/Fallback error for {candidate.get('symbol', 'unknown')}: {error}")
576
- traceback.print_exc()
577
 
578
- print("✅ Cycle finished. No actionable BUY/SELL opportunities found by LLM.")
579
  return None
580
 
581
  except Exception as error:
582
- print(f" An error occurred while scanning for opportunities: {error}")
583
- traceback.print_exc()
584
  await r2_service_global.save_system_logs_async({
585
  "opportunity_scan_error": True,
586
- "error": str(error),
587
- "enhanced_analysis": True
588
  })
589
  return None
590
 
@@ -596,34 +430,18 @@ async def re_analyze_open_trade_async(trade_data):
596
  current_time = datetime.now()
597
  hold_minutes = (current_time - entry_time).total_seconds() / 60
598
 
599
- print(f"⏳ Re-analyzing trade: {symbol} (held for {hold_minutes:.1f} minutes)")
600
-
601
  original_strategy = trade_data.get('strategy')
602
  if not original_strategy or original_strategy == 'unknown':
603
  original_strategy = trade_data.get('decision_data', {}).get('strategy', 'GENERIC')
604
- print(f"🔧 Fixed original strategy for {symbol}: {original_strategy}")
605
-
606
- reanalysis_context = {
607
- 'trade_data': {
608
- 'symbol': trade_data.get('symbol'),
609
- 'entry_price': trade_data.get('entry_price'),
610
- 'entry_time': trade_data.get('entry_timestamp'),
611
- 'hold_minutes': hold_minutes,
612
- 'strategy': original_strategy
613
- }
614
- }
615
-
616
  try:
617
  market_context = await data_manager_global.get_market_context_async()
618
- except Exception as error:
619
- print(f"⚠️ Failed to get market context: {error}. Using basic market data...")
620
  market_context = {'btc_sentiment': 'NEUTRAL'}
621
 
622
  symbol_with_reasons = [{'symbol': symbol, 'reasons': ['re-analysis']}]
623
-
624
  ohlcv_data_list = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
625
  if not ohlcv_data_list:
626
- print(f"❌ Failed to fetch latest data for {symbol}.")
627
  return None
628
 
629
  raw_data = ohlcv_data_list[0]
@@ -631,50 +449,39 @@ async def re_analyze_open_trade_async(trade_data):
631
  updated_market_context = await data_manager_global.get_market_context_async()
632
  if updated_market_context:
633
  market_context = updated_market_context
634
- except Exception as e:
635
- print(f"⚠️ Failed to update market context for re-analysis: {e}")
636
 
637
  feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
638
  processed_data = await feature_processor.process_and_score_symbol(raw_data)
639
 
640
  if not processed_data:
641
- print(f"❌ Failed to process latest data for {symbol}.")
642
  return None
643
 
644
  await r2_service_global.save_candidates_data_async(
645
  candidates_data=None,
646
  reanalysis_data={
647
  'market_context': market_context,
648
- 'processed_data': processed_data,
649
- 'enhanced_analysis': True
650
  }
651
  )
652
 
653
- print(f"🧠 Getting LLM re-analysis for {symbol} with enhanced data...")
654
  try:
655
  re_analysis_decision = await llm_service_global.re_analyze_trade_async(trade_data, processed_data)
656
- source = re_analysis_decision.get('model_source', 'LLM')
657
- except Exception as error:
658
- print(f"❌ LLM re-analysis error: {error}. Falling back to local.")
659
  re_analysis_decision = local_re_analyze_trade(trade_data, processed_data)
660
- source = 'local_fallback'
661
 
662
  final_decision = _apply_patience_logic(re_analysis_decision, hold_minutes, trade_data, processed_data)
663
 
664
- if not final_decision.get('strategy') or final_decision['strategy'] == 'unknown':
665
  final_decision['strategy'] = original_strategy
666
- print(f"🔧 Final re-analysis strategy fix for {symbol}: {original_strategy}")
667
-
668
- print(f"✅ Re-analysis decision for {symbol}: {final_decision.get('action')}. Strategy: {final_decision.get('strategy')}. Source: {source}")
669
 
670
  await r2_service_global.save_system_logs_async({
671
  "trade_reanalyzed": True,
672
  "symbol": symbol,
673
  "action": final_decision.get('action'),
674
- "hold_minutes": hold_minutes,
675
- "source": source,
676
- "strategy": final_decision.get('strategy'),
677
- "enhanced_analysis": True
678
  })
679
 
680
  return {
@@ -685,35 +492,26 @@ async def re_analyze_open_trade_async(trade_data):
685
  }
686
 
687
  except Exception as error:
688
- print(f" An error occurred during trade re-analysis: {error}")
689
- traceback.print_exc()
690
  await r2_service_global.save_system_logs_async({
691
  "reanalysis_error": True,
692
  "symbol": symbol,
693
- "error": str(error),
694
- "enhanced_analysis": True
695
  })
696
  return None
697
 
698
  async def run_bot_cycle_async():
699
- print(f"\n{'='*70}")
700
- print(f"⏳ New cycle initiated at: {datetime.now().isoformat()}")
701
- print(f"{'='*70}")
702
-
703
  try:
704
  await r2_service_global.save_system_logs_async({
705
- "cycle_started": True,
706
- "enhanced_analysis": True
707
  })
708
 
709
  if not r2_service_global.acquire_lock():
710
- print("❌ Failed to acquire lock. Skipping cycle.")
711
  return
712
 
713
  open_trades = []
714
  try:
715
  open_trades = await r2_service_global.get_open_trades_async()
716
- print(f"✅ Found {len(open_trades)} open trade(s).")
717
 
718
  trades_fixed = 0
719
  for trade in open_trades:
@@ -721,10 +519,8 @@ async def run_bot_cycle_async():
721
  original_strategy = trade.get('decision_data', {}).get('strategy', 'GENERIC')
722
  trade['strategy'] = original_strategy
723
  trades_fixed += 1
724
- print(f"🔧 Fixed missing strategy for {trade['symbol']}: {trade['strategy']}")
725
 
726
  if trades_fixed > 0:
727
- print(f"✅ Fixed strategies for {trades_fixed} trades.")
728
  await r2_service_global.save_open_trades_async(open_trades)
729
 
730
  should_look_for_new_trade = not open_trades
@@ -737,12 +533,10 @@ async def run_bot_cycle_async():
737
  ]
738
 
739
  if trades_to_reanalyze:
740
- print(f"🔍 Re-analyzing {len(trades_to_reanalyze)} trade(s)...")
741
  for trade in trades_to_reanalyze:
742
  result = await re_analyze_open_trade_async(trade)
743
  if result and result['decision'].get('action') == "CLOSE_TRADE":
744
  await r2_service_global.close_trade_async(trade, result['current_price'])
745
- print(f"✅ Trade for {trade['symbol']} CLOSED. Strategy: {trade.get('strategy', 'unknown')}")
746
  if learning_engine_global and learning_engine_global.initialized:
747
  trade_with_strategy = trade.copy()
748
  strategy = result['decision'].get('strategy', trade.get('strategy', 'GENERIC'))
@@ -751,42 +545,25 @@ async def run_bot_cycle_async():
751
  should_look_for_new_trade = True
752
  elif result and result['decision'].get('action') == "UPDATE_TRADE":
753
  await r2_service_global.update_trade_async(trade, result['decision'])
754
- print(f"✅ Trade for {trade['symbol']} UPDATED. Strategy: {trade.get('strategy', 'unknown')}")
755
- else:
756
- print(f"✅ Trade for {trade['symbol']} is on HOLD. Strategy: {trade.get('strategy', 'unknown')}")
757
- else:
758
- print("✅ No trades due for re-analysis yet.")
759
 
760
  if should_look_for_new_trade:
761
  portfolio_state = await r2_service_global.get_portfolio_state_async()
762
  current_capital = portfolio_state.get("current_capital_usd", 0)
763
 
764
- print(f"💰 Current available capital: ${current_capital:.2f}")
765
-
766
  if current_capital <= 0:
767
- print("⚠️ Current capital is 0. Checking for potential calculation errors...")
768
-
769
  if len(open_trades) == 0:
770
- print("🔄 No open trades but capital is 0. This might be an error.")
771
- print("💡 Attempting to recover capital state...")
772
-
773
  initial_capital = portfolio_state.get("initial_capital_usd", 10.0)
774
  if initial_capital > 0:
775
  portfolio_state["current_capital_usd"] = initial_capital
776
  portfolio_state["invested_capital_usd"] = 0.0
777
  await r2_service_global.save_portfolio_state_async(portfolio_state)
778
- print(f"✅ Reset capital to initial amount: ${initial_capital:.2f}")
779
  current_capital = initial_capital
780
 
781
  if current_capital > 1:
782
- print(f"✅ Capital available (${current_capital:.2f}). Scanning for new opportunities...")
783
  new_opportunity = await find_new_opportunities_async()
784
  if new_opportunity:
785
- print(f"✅ Opportunity for {new_opportunity['symbol']} confirmed! Saving trade. Strategy: {new_opportunity.get('strategy')}")
786
-
787
  if not new_opportunity['decision'].get('strategy'):
788
  new_opportunity['decision']['strategy'] = new_opportunity.get('strategy', 'GENERIC')
789
- print(f"🔧 Final pre-save strategy fix: {new_opportunity['decision']['strategy']}")
790
 
791
  await r2_service_global.save_new_trade_async(
792
  new_opportunity['symbol'],
@@ -798,35 +575,26 @@ async def run_bot_cycle_async():
798
  if trade['symbol'] == new_opportunity['symbol']:
799
  asyncio.create_task(realtime_monitor._monitor_single_trade(trade))
800
  break
801
- else:
802
- print("✅ Scan complete. No actionable opportunities identified.")
803
- else:
804
- print(f"😴 No available capital (${current_capital:.2f}). Waiting for current trade to close.")
805
 
806
  finally:
807
- print("✅ Cycle finished. Releasing lock.")
808
  r2_service_global.release_lock()
809
  await r2_service_global.save_system_logs_async({
810
  "cycle_completed": True,
811
- "open_trades": len(open_trades),
812
- "enhanced_analysis": True
813
  })
814
 
815
  except Exception as error:
816
- print(f"Unhandled error in main cycle: {error}")
817
- traceback.print_exc()
818
  await r2_service_global.save_system_logs_async({
819
  "cycle_error": True,
820
- "error": str(error),
821
- "enhanced_analysis": True
822
  })
823
  if r2_service_global.lock_acquired:
824
  r2_service_global.release_lock()
825
 
826
  @asynccontextmanager
827
  async def lifespan(application: FastAPI):
828
- global r2_service_global, data_manager_global, llm_service_global, learning_engine_global, realtime_monitor
829
- print("===== Application Startup =====")
830
 
831
  try:
832
  r2_service_global = R2Service()
@@ -836,36 +604,30 @@ async def lifespan(application: FastAPI):
836
  data_manager_global = DataManager(contracts_database)
837
  await data_manager_global.initialize()
838
 
 
 
839
  learning_engine_global = LearningEngine(r2_service_global, data_manager_global)
840
  await learning_engine_global.initialize_enhanced()
841
 
842
  await learning_engine_global.force_strategy_learning()
843
 
844
- if learning_engine_global.initialized:
845
- weights = await learning_engine_global.get_optimized_strategy_weights("bull_market")
846
- print(f"🎯 الأوزان المحملة: {weights}")
847
-
848
  realtime_monitor = RealTimeTradeMonitor()
849
 
850
  asyncio.create_task(monitor_market_async())
851
  asyncio.create_task(realtime_monitor.start_monitoring())
852
 
853
  await r2_service_global.save_system_logs_async({
854
- "application_started": True,
855
- "enhanced_analysis": True
856
  })
857
 
858
- print("\n✅ All services initialized. Application is ready.\n")
859
  yield
860
 
861
  except Exception as error:
862
- print(f"Application startup failed: {error}")
863
- traceback.print_exc()
864
  if r2_service_global:
865
  await r2_service_global.save_system_logs_async({
866
  "application_startup_failed": True,
867
- "error": str(error),
868
- "enhanced_analysis": True
869
  })
870
  raise
871
  finally:
@@ -876,7 +638,7 @@ application = FastAPI(lifespan=lifespan)
876
  @application.get("/run-cycle")
877
  async def run_cycle_api():
878
  asyncio.create_task(run_bot_cycle_async())
879
- return {"message": "Bot cycle initiated in the background."}
880
 
881
  @application.get("/health")
882
  async def health_check():
@@ -899,8 +661,7 @@ async def health_check():
899
  "realtime_monitor": "running" if realtime_monitor and realtime_monitor.is_running else "stopped"
900
  },
901
  "market_state_ok": state.MARKET_STATE_OK,
902
- "learning_engine": learning_metrics,
903
- "api_usage_stats": api_stats.get('api_usage', {})
904
  }
905
 
906
  @application.get("/stats")
@@ -909,10 +670,8 @@ async def get_performance_stats():
909
  market_context = await data_manager_global.get_market_context_async() if data_manager_global else {}
910
 
911
  learning_stats = {}
912
- improvement_suggestions = []
913
  if learning_engine_global and learning_engine_global.initialized:
914
  learning_stats = await learning_engine_global.calculate_performance_metrics()
915
- improvement_suggestions = await learning_engine_global.suggest_improvements()
916
 
917
  api_stats = {}
918
  if data_manager_global:
@@ -923,20 +682,13 @@ async def get_performance_stats():
923
  "data_manager": api_stats,
924
  "market_state": {
925
  "is_healthy": state.MARKET_STATE_OK,
926
- "description": "Market is healthy for trading" if state.MARKET_STATE_OK else "Market conditions are unfavorable",
927
  "context": market_context
928
  },
929
  "realtime_monitoring": {
930
  "active_trades": len(realtime_monitor.monitoring_tasks) if realtime_monitor else 0,
931
  "is_running": realtime_monitor.is_running if realtime_monitor else False
932
  },
933
- "learning_engine": learning_stats,
934
- "improvement_suggestions": improvement_suggestions,
935
- "enhanced_features": {
936
- "netflow_analysis": True,
937
- "enhanced_whale_tracking": True,
938
- "dynamic_strategy_selection": True
939
- }
940
  }
941
  return stats
942
  except Exception as error:
@@ -953,45 +705,39 @@ async def get_logs_status():
953
  "open_trades_count": len(open_trades),
954
  "current_capital": portfolio_state.get("current_capital_usd", 0),
955
  "total_trades": portfolio_state.get("total_trades", 0),
956
- "timestamp": datetime.now().isoformat(),
957
- "enhanced_analysis": True
958
  }
959
  except Exception as error:
960
  raise HTTPException(status_code=500, detail=f"Failed to get logs status: {str(error)}")
961
 
962
  async def cleanup_on_shutdown():
963
  global r2_service_global, data_manager_global, realtime_monitor, learning_engine_global
964
- print("\n🛑 Shutdown signal received. Cleaning up...")
965
 
966
  if r2_service_global:
967
  try:
968
  await r2_service_global.save_system_logs_async({
969
- "application_shutdown": True,
970
- "enhanced_analysis": True
971
  })
972
- except Exception as log_error:
973
- print(f"⚠️ Failed to save shutdown log: {log_error}")
974
 
975
  if learning_engine_global and learning_engine_global.initialized:
976
  try:
977
  await learning_engine_global.save_weights_to_r2()
978
  await learning_engine_global.save_performance_history()
979
- print("✅ Learning engine data saved.")
980
- except Exception as e:
981
- print(f"⚠️ Failed to save learning engine data: {e}")
982
 
983
  if realtime_monitor:
984
  realtime_monitor.stop_monitoring()
985
 
986
  if r2_service_global and r2_service_global.lock_acquired:
987
  r2_service_global.release_lock()
988
- print("✅ Lock released.")
989
  if data_manager_global:
990
  await data_manager_global.close()
991
- print("✅ Cleanup completed.")
992
 
993
  def signal_handler(signum, frame):
994
- print(f"\n⚠️ Received signal {signum}")
995
  asyncio.create_task(cleanup_on_shutdown())
996
  sys.exit(0)
997
 
 
1
+ import os, traceback, signal, sys, uvicorn, asyncio, json
 
 
 
 
 
2
  from contextlib import asynccontextmanager
3
+ from fastapi import FastAPI, HTTPException
4
+ from datetime import datetime
5
  from r2 import R2Service
6
  from LLM import LLMService, local_analyze_opportunity, local_re_analyze_trade
7
  from data_manager import DataManager
8
  from ML import MLProcessor as FeatureProcessor
9
  from learning_engine import LearningEngine
10
+ from sentiment_news import SentimentAnalyzer
 
11
  import state
 
 
 
12
  from helpers import safe_float_conversion, _apply_patience_logic
13
 
 
 
 
 
14
  r2_service_global = None
15
  data_manager_global = None
16
  llm_service_global = None
17
  learning_engine_global = None
18
  realtime_monitor = None
19
+ sentiment_analyzer_global = None
20
 
21
  class RealTimeTradeMonitor:
22
  def __init__(self):
 
25
 
26
  async def start_monitoring(self):
27
  self.is_running = True
 
28
 
29
  while self.is_running:
30
  try:
 
44
  await asyncio.sleep(10)
45
 
46
  except Exception as error:
47
+ print(f"Real-time monitor error: {error}")
48
  await asyncio.sleep(30)
49
 
50
  async def _monitor_single_trade(self, trade):
51
  symbol = trade['symbol']
 
 
52
 
53
  while symbol in self.monitoring_tasks and self.is_running:
54
  try:
 
75
  dynamic_stop = current_price * 0.98
76
  if dynamic_stop > (stop_loss or 0):
77
  trade['stop_loss'] = dynamic_stop
 
78
 
79
  if should_close:
 
 
80
  if r2_service_global.acquire_lock():
81
  try:
82
  await r2_service_global.close_trade_async(trade, current_price)
 
83
 
84
  if learning_engine_global and learning_engine_global.initialized:
85
  await learning_engine_global.analyze_trade_outcome(trade, 'CLOSED_BY_MONITOR')
 
96
  await asyncio.sleep(15)
97
 
98
  except Exception as error:
99
+ print(f"Real-time monitoring error for {symbol}: {error}")
100
  await asyncio.sleep(30)
101
 
102
  def stop_monitoring(self):
103
  self.is_running = False
104
  self.monitoring_tasks.clear()
 
105
 
106
  async def monitor_market_async():
107
+ global data_manager_global, sentiment_analyzer_global
108
 
109
  init_attempts = 0
110
  while data_manager_global is None and init_attempts < 10:
 
111
  await asyncio.sleep(3)
112
  init_attempts += 1
113
 
114
  if data_manager_global is None:
 
115
  return
116
 
117
  while True:
118
  try:
119
+ market_context = await sentiment_analyzer_global.get_market_sentiment()
 
 
 
 
 
 
120
 
121
  if not market_context:
 
122
  state.MARKET_STATE_OK = True
123
  await asyncio.sleep(60)
124
  continue
125
 
126
  whale_analysis = market_context.get('general_whale_activity', {})
 
127
  is_critical = whale_analysis.get('critical_alert', False)
 
 
 
 
 
 
 
 
 
 
128
 
129
  bitcoin_sentiment = market_context.get('btc_sentiment')
130
  fear_greed_index = market_context.get('fear_and_greed_index')
 
134
 
135
  if is_critical:
136
  should_halt_trading = True
137
+ halt_reason = f"CRITICAL whale activity detected"
138
  elif bitcoin_sentiment == 'BEARISH' and (fear_greed_index is not None and fear_greed_index < 30):
139
  should_halt_trading = True
140
+ halt_reason = f"Bearish market conditions"
 
 
 
141
 
142
  if should_halt_trading:
 
143
  state.MARKET_STATE_OK = False
144
+ await r2_service_global.save_system_logs_async({
145
+ "market_halt": True,
146
+ "reason": halt_reason
147
+ })
 
 
 
 
 
 
 
 
148
  else:
149
  if not state.MARKET_STATE_OK:
150
+ print("Market conditions improved. Resuming normal operations.")
151
  state.MARKET_STATE_OK = True
152
 
153
  await asyncio.sleep(60)
154
  except Exception as error:
155
+ print(f"Error during market monitoring: {error}")
 
156
  state.MARKET_STATE_OK = True
157
  await asyncio.sleep(60)
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  async def validate_candidate_data_enhanced(candidate):
160
  try:
161
  required_fields = ['symbol', 'current_price', 'final_score', 'enhanced_final_score']
 
168
  candidate['final_score'] = safe_float_conversion(candidate.get('final_score'), 0.5)
169
  candidate['enhanced_final_score'] = safe_float_conversion(candidate.get('enhanced_final_score'), candidate['final_score'])
170
 
171
+ if 'reasons_for_candidacy' not in candidate:
172
  candidate['reasons_for_candidacy'] = ['unknown_reason']
173
 
174
+ if 'sentiment_data' not in candidate:
175
  candidate['sentiment_data'] = {
176
  'btc_sentiment': 'NEUTRAL',
177
  'fear_and_greed_index': 50,
 
184
  if 'strategy_scores' not in candidate:
185
  candidate['strategy_scores'] = {}
186
 
187
+ if 'target_strategy' not in candidate:
 
 
 
188
  candidate['target_strategy'] = 'GENERIC'
189
 
190
  return True
191
 
192
  except Exception as error:
193
+ print(f"Failed to validate candidate data for {candidate.get('symbol')}: {error}")
194
  return False
195
 
196
  async def analyze_market_strategy(market_context):
197
  try:
198
  whale_analysis = market_context.get('general_whale_activity', {})
199
  netflow_analysis = whale_analysis.get('netflow_analysis', {})
 
200
 
201
  prompt = f"""
202
+ Analyze current market conditions and determine trading strategy.
203
 
204
+ Market Data:
 
205
  - BTC Sentiment: {market_context.get('btc_sentiment')}
206
  - Fear & Greed Index: {market_context.get('fear_and_greed_index')}
207
  - Whale Analysis: {whale_analysis.get('sentiment')}
208
+ - Critical Alert: {whale_analysis.get('critical_alert')}
209
+ - Net Flow: ${netflow_analysis.get('net_flow', 0):,.0f}
210
+
211
+ Output JSON:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  {{
213
  "primary_strategy": "STRATEGY_NAME",
214
+ "reasoning": "Brief reasoning",
215
  "risk_tolerance": 5,
216
  "optimal_scan_count": 100
217
  }}
 
220
  response = await llm_service_global._call_llm(prompt)
221
 
222
  try:
223
+ from helpers import parse_json_from_response
224
+ json_str = parse_json_from_response(response)
225
+ strategy_data = json.loads(json_str)
226
  except:
227
  net_flow = netflow_analysis.get('net_flow', 0)
228
  if net_flow > 1000000:
229
  fallback_strategy = "AGGRESSIVE_GROWTH"
 
230
  elif net_flow < -1000000:
231
  fallback_strategy = "CONSERVATIVE"
 
232
  elif whale_analysis.get('critical_alert'):
233
  fallback_strategy = "CONSERVATIVE"
 
234
  else:
235
  fallback_strategy = "GENERIC"
 
236
 
237
  strategy_data = {
238
  "primary_strategy": fallback_strategy,
239
+ "reasoning": "Fallback strategy",
240
  "risk_tolerance": 5,
241
  "optimal_scan_count": 100,
242
  }
 
244
  return strategy_data
245
 
246
  except Exception as error:
247
+ print(f"Failed to analyze market strategy: {error}")
248
  return {
249
  "primary_strategy": "GENERIC",
250
  "reasoning": "Fallback due to analysis error",
 
257
  all_candidates = await data_manager_global.find_high_potential_candidates(scan_count * 2)
258
 
259
  if not all_candidates:
 
260
  return []
261
 
262
  market_context = await data_manager_global.get_market_context_async()
263
  if not market_context:
 
264
  return []
265
 
266
  feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
 
272
  ohlcv_data = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
273
 
274
  if ohlcv_data and ohlcv_data[0]:
 
 
 
 
 
 
 
275
  processed = await feature_processor.process_and_score_symbol_enhanced(ohlcv_data[0])
276
  if processed:
277
  processed_candidates.append(processed)
278
  except Exception as e:
279
+ print(f"Failed to process {candidate.get('symbol')}: {e}")
280
 
281
  if not processed_candidates:
 
282
  return []
283
 
284
  if strategy != 'GENERIC':
 
290
  if strategy_score > 0.2:
291
  candidate['strategy_match_score'] = strategy_score
292
  strategy_candidates.append(candidate)
 
293
 
294
  sorted_candidates = sorted(strategy_candidates,
295
  key=lambda x: x.get('strategy_match_score', 0),
296
  reverse=True)
297
  top_candidates = sorted_candidates[:15]
 
 
298
  else:
299
  sorted_candidates = sorted(processed_candidates,
300
  key=lambda x: x.get('enhanced_final_score', 0),
301
  reverse=True)
302
  top_candidates = sorted_candidates[:15]
 
303
 
304
  return top_candidates
305
 
306
  except Exception as error:
307
+ print(f"Advanced filtering failed: {error}")
 
308
  return []
309
 
310
  async def find_new_opportunities_async():
 
311
  try:
312
  await r2_service_global.save_system_logs_async({
313
+ "opportunity_scan_started": True
314
  })
315
 
 
316
  market_context = await data_manager_global.get_market_context_async()
317
  if not market_context:
 
318
  return
319
 
320
  strategy_decision = await analyze_market_strategy(market_context)
321
 
 
 
 
 
 
 
322
  high_potential_candidates = await find_strategy_specific_candidates(
323
  strategy_decision['primary_strategy'],
324
  strategy_decision.get('optimal_scan_count', 100)
325
  )
326
 
327
  if not high_potential_candidates:
 
328
  high_potential_candidates = await data_manager_global.find_high_potential_candidates(20)
329
  if high_potential_candidates:
330
  for candidate in high_potential_candidates:
331
  candidate['target_strategy'] = 'GENERIC'
 
332
  else:
 
 
 
 
 
333
  return
334
 
335
  all_processed_candidates = []
336
+ CHUNK_SIZE = 5
337
+
338
  for index in range(0, len(high_potential_candidates), CHUNK_SIZE):
339
  chunk = high_potential_candidates[index:index+CHUNK_SIZE]
 
340
  chunk_data = await data_manager_global.get_fast_pass_data_async(chunk)
341
 
 
342
  updated_market_context = await data_manager_global.get_market_context_async()
343
  if not updated_market_context:
344
  updated_market_context = market_context
 
353
  await asyncio.sleep(1)
354
 
355
  if not all_processed_candidates:
 
356
  return
357
 
358
  updated_market_context = await data_manager_global.get_market_context_async()
 
360
  updated_market_context = market_context
361
 
362
  feature_processor = FeatureProcessor(updated_market_context, data_manager_global, learning_engine_global)
363
+ OPPORTUNITY_COUNT = 10
364
  top_candidates = feature_processor.filter_top_candidates(all_processed_candidates, OPPORTUNITY_COUNT)
365
 
 
 
366
  await r2_service_global.save_candidates_data_async(
367
  candidates_data=top_candidates,
368
  reanalysis_data={
369
  "strategy_used": strategy_decision,
370
+ "market_conditions": market_context
 
371
  }
372
  )
373
 
374
  if not top_candidates:
 
 
 
 
 
 
 
375
  return
376
 
 
 
377
  for candidate in top_candidates:
378
  try:
379
  if not await validate_candidate_data_enhanced(candidate):
 
380
  continue
381
 
382
  llm_analysis_data = await llm_service_global.get_trading_decision(candidate)
383
 
384
  if not llm_analysis_data:
 
385
  continue
386
 
387
  if llm_analysis_data.get('action') == "HOLD":
 
388
  continue
389
 
390
  if llm_analysis_data.get('action') in ["BUY", "SELL"]:
391
  final_strategy = llm_analysis_data.get('strategy')
392
  candidate_strategy = candidate.get('target_strategy', 'GENERIC')
393
 
394
+ if not final_strategy or final_strategy == 'unknown':
395
  final_strategy = candidate_strategy
396
  llm_analysis_data['strategy'] = final_strategy
 
 
 
 
 
 
 
 
 
 
 
 
397
 
398
  await r2_service_global.save_system_logs_async({
399
  "new_opportunity_found": True,
400
  "symbol": candidate['symbol'],
401
  "action": llm_analysis_data.get('action'),
402
+ "strategy": final_strategy
 
 
403
  })
404
 
405
  return {
 
410
  }
411
 
412
  except Exception as error:
413
+ print(f"LLM error for {candidate.get('symbol', 'unknown')}: {error}")
 
414
 
 
415
  return None
416
 
417
  except Exception as error:
418
+ print(f"Error while scanning for opportunities: {error}")
 
419
  await r2_service_global.save_system_logs_async({
420
  "opportunity_scan_error": True,
421
+ "error": str(error)
 
422
  })
423
  return None
424
 
 
430
  current_time = datetime.now()
431
  hold_minutes = (current_time - entry_time).total_seconds() / 60
432
 
 
 
433
  original_strategy = trade_data.get('strategy')
434
  if not original_strategy or original_strategy == 'unknown':
435
  original_strategy = trade_data.get('decision_data', {}).get('strategy', 'GENERIC')
436
+
 
 
 
 
 
 
 
 
 
 
 
437
  try:
438
  market_context = await data_manager_global.get_market_context_async()
439
+ except Exception:
 
440
  market_context = {'btc_sentiment': 'NEUTRAL'}
441
 
442
  symbol_with_reasons = [{'symbol': symbol, 'reasons': ['re-analysis']}]
 
443
  ohlcv_data_list = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
444
  if not ohlcv_data_list:
 
445
  return None
446
 
447
  raw_data = ohlcv_data_list[0]
 
449
  updated_market_context = await data_manager_global.get_market_context_async()
450
  if updated_market_context:
451
  market_context = updated_market_context
452
+ except Exception:
453
+ pass
454
 
455
  feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
456
  processed_data = await feature_processor.process_and_score_symbol(raw_data)
457
 
458
  if not processed_data:
 
459
  return None
460
 
461
  await r2_service_global.save_candidates_data_async(
462
  candidates_data=None,
463
  reanalysis_data={
464
  'market_context': market_context,
465
+ 'processed_data': processed_data
 
466
  }
467
  )
468
 
 
469
  try:
470
  re_analysis_decision = await llm_service_global.re_analyze_trade_async(trade_data, processed_data)
471
+ except Exception:
 
 
472
  re_analysis_decision = local_re_analyze_trade(trade_data, processed_data)
 
473
 
474
  final_decision = _apply_patience_logic(re_analysis_decision, hold_minutes, trade_data, processed_data)
475
 
476
+ if not final_decision.get('strategy'):
477
  final_decision['strategy'] = original_strategy
 
 
 
478
 
479
  await r2_service_global.save_system_logs_async({
480
  "trade_reanalyzed": True,
481
  "symbol": symbol,
482
  "action": final_decision.get('action'),
483
+ "hold_minutes": hold_minutes,
484
+ "strategy": final_decision.get('strategy')
 
 
485
  })
486
 
487
  return {
 
492
  }
493
 
494
  except Exception as error:
495
+ print(f"Error during trade re-analysis: {error}")
 
496
  await r2_service_global.save_system_logs_async({
497
  "reanalysis_error": True,
498
  "symbol": symbol,
499
+ "error": str(error)
 
500
  })
501
  return None
502
 
503
  async def run_bot_cycle_async():
 
 
 
 
504
  try:
505
  await r2_service_global.save_system_logs_async({
506
+ "cycle_started": True
 
507
  })
508
 
509
  if not r2_service_global.acquire_lock():
 
510
  return
511
 
512
  open_trades = []
513
  try:
514
  open_trades = await r2_service_global.get_open_trades_async()
 
515
 
516
  trades_fixed = 0
517
  for trade in open_trades:
 
519
  original_strategy = trade.get('decision_data', {}).get('strategy', 'GENERIC')
520
  trade['strategy'] = original_strategy
521
  trades_fixed += 1
 
522
 
523
  if trades_fixed > 0:
 
524
  await r2_service_global.save_open_trades_async(open_trades)
525
 
526
  should_look_for_new_trade = not open_trades
 
533
  ]
534
 
535
  if trades_to_reanalyze:
 
536
  for trade in trades_to_reanalyze:
537
  result = await re_analyze_open_trade_async(trade)
538
  if result and result['decision'].get('action') == "CLOSE_TRADE":
539
  await r2_service_global.close_trade_async(trade, result['current_price'])
 
540
  if learning_engine_global and learning_engine_global.initialized:
541
  trade_with_strategy = trade.copy()
542
  strategy = result['decision'].get('strategy', trade.get('strategy', 'GENERIC'))
 
545
  should_look_for_new_trade = True
546
  elif result and result['decision'].get('action') == "UPDATE_TRADE":
547
  await r2_service_global.update_trade_async(trade, result['decision'])
 
 
 
 
 
548
 
549
  if should_look_for_new_trade:
550
  portfolio_state = await r2_service_global.get_portfolio_state_async()
551
  current_capital = portfolio_state.get("current_capital_usd", 0)
552
 
 
 
553
  if current_capital <= 0:
 
 
554
  if len(open_trades) == 0:
 
 
 
555
  initial_capital = portfolio_state.get("initial_capital_usd", 10.0)
556
  if initial_capital > 0:
557
  portfolio_state["current_capital_usd"] = initial_capital
558
  portfolio_state["invested_capital_usd"] = 0.0
559
  await r2_service_global.save_portfolio_state_async(portfolio_state)
 
560
  current_capital = initial_capital
561
 
562
  if current_capital > 1:
 
563
  new_opportunity = await find_new_opportunities_async()
564
  if new_opportunity:
 
 
565
  if not new_opportunity['decision'].get('strategy'):
566
  new_opportunity['decision']['strategy'] = new_opportunity.get('strategy', 'GENERIC')
 
567
 
568
  await r2_service_global.save_new_trade_async(
569
  new_opportunity['symbol'],
 
575
  if trade['symbol'] == new_opportunity['symbol']:
576
  asyncio.create_task(realtime_monitor._monitor_single_trade(trade))
577
  break
 
 
 
 
578
 
579
  finally:
 
580
  r2_service_global.release_lock()
581
  await r2_service_global.save_system_logs_async({
582
  "cycle_completed": True,
583
+ "open_trades": len(open_trades)
 
584
  })
585
 
586
  except Exception as error:
587
+ print(f"Unhandled error in main cycle: {error}")
 
588
  await r2_service_global.save_system_logs_async({
589
  "cycle_error": True,
590
+ "error": str(error)
 
591
  })
592
  if r2_service_global.lock_acquired:
593
  r2_service_global.release_lock()
594
 
595
  @asynccontextmanager
596
  async def lifespan(application: FastAPI):
597
+ global r2_service_global, data_manager_global, llm_service_global, learning_engine_global, realtime_monitor, sentiment_analyzer_global
 
598
 
599
  try:
600
  r2_service_global = R2Service()
 
604
  data_manager_global = DataManager(contracts_database)
605
  await data_manager_global.initialize()
606
 
607
+ sentiment_analyzer_global = SentimentAnalyzer(data_manager_global)
608
+
609
  learning_engine_global = LearningEngine(r2_service_global, data_manager_global)
610
  await learning_engine_global.initialize_enhanced()
611
 
612
  await learning_engine_global.force_strategy_learning()
613
 
 
 
 
 
614
  realtime_monitor = RealTimeTradeMonitor()
615
 
616
  asyncio.create_task(monitor_market_async())
617
  asyncio.create_task(realtime_monitor.start_monitoring())
618
 
619
  await r2_service_global.save_system_logs_async({
620
+ "application_started": True
 
621
  })
622
 
 
623
  yield
624
 
625
  except Exception as error:
626
+ print(f"Application startup failed: {error}")
 
627
  if r2_service_global:
628
  await r2_service_global.save_system_logs_async({
629
  "application_startup_failed": True,
630
+ "error": str(error)
 
631
  })
632
  raise
633
  finally:
 
638
  @application.get("/run-cycle")
639
  async def run_cycle_api():
640
  asyncio.create_task(run_bot_cycle_async())
641
+ return {"message": "Bot cycle initiated"}
642
 
643
  @application.get("/health")
644
  async def health_check():
 
661
  "realtime_monitor": "running" if realtime_monitor and realtime_monitor.is_running else "stopped"
662
  },
663
  "market_state_ok": state.MARKET_STATE_OK,
664
+ "learning_engine": learning_metrics
 
665
  }
666
 
667
  @application.get("/stats")
 
670
  market_context = await data_manager_global.get_market_context_async() if data_manager_global else {}
671
 
672
  learning_stats = {}
 
673
  if learning_engine_global and learning_engine_global.initialized:
674
  learning_stats = await learning_engine_global.calculate_performance_metrics()
 
675
 
676
  api_stats = {}
677
  if data_manager_global:
 
682
  "data_manager": api_stats,
683
  "market_state": {
684
  "is_healthy": state.MARKET_STATE_OK,
 
685
  "context": market_context
686
  },
687
  "realtime_monitoring": {
688
  "active_trades": len(realtime_monitor.monitoring_tasks) if realtime_monitor else 0,
689
  "is_running": realtime_monitor.is_running if realtime_monitor else False
690
  },
691
+ "learning_engine": learning_stats
 
 
 
 
 
 
692
  }
693
  return stats
694
  except Exception as error:
 
705
  "open_trades_count": len(open_trades),
706
  "current_capital": portfolio_state.get("current_capital_usd", 0),
707
  "total_trades": portfolio_state.get("total_trades", 0),
708
+ "timestamp": datetime.now().isoformat()
 
709
  }
710
  except Exception as error:
711
  raise HTTPException(status_code=500, detail=f"Failed to get logs status: {str(error)}")
712
 
713
  async def cleanup_on_shutdown():
714
  global r2_service_global, data_manager_global, realtime_monitor, learning_engine_global
715
+ print("Shutdown signal received. Cleaning up...")
716
 
717
  if r2_service_global:
718
  try:
719
  await r2_service_global.save_system_logs_async({
720
+ "application_shutdown": True
 
721
  })
722
+ except Exception:
723
+ pass
724
 
725
  if learning_engine_global and learning_engine_global.initialized:
726
  try:
727
  await learning_engine_global.save_weights_to_r2()
728
  await learning_engine_global.save_performance_history()
729
+ except Exception:
730
+ pass
 
731
 
732
  if realtime_monitor:
733
  realtime_monitor.stop_monitoring()
734
 
735
  if r2_service_global and r2_service_global.lock_acquired:
736
  r2_service_global.release_lock()
 
737
  if data_manager_global:
738
  await data_manager_global.close()
 
739
 
740
  def signal_handler(signum, frame):
 
741
  asyncio.create_task(cleanup_on_shutdown())
742
  sys.exit(0)
743