Riy777 commited on
Commit
49edbc8
·
1 Parent(s): 1b0c9db

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -428
app.py CHANGED
@@ -3,13 +3,13 @@ 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
@@ -25,76 +25,48 @@ class RealTimeTradeMonitor:
25
 
26
  async def start_monitoring(self):
27
  self.is_running = True
28
-
29
  while self.is_running:
30
  try:
31
  open_trades = await r2_service_global.get_open_trades_async()
32
-
33
  for trade in open_trades:
34
  symbol = trade['symbol']
35
  if symbol not in self.monitoring_tasks:
36
  asyncio.create_task(self._monitor_single_trade(trade))
37
  self.monitoring_tasks[symbol] = trade
38
-
39
  current_symbols = {trade['symbol'] for trade in open_trades}
40
  for symbol in list(self.monitoring_tasks.keys()):
41
- if symbol not in current_symbols:
42
- del self.monitoring_tasks[symbol]
43
-
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:
55
  current_price = await data_manager_global.get_latest_price_async(symbol)
56
- if not current_price:
57
- await asyncio.sleep(15)
58
- continue
59
-
60
  entry_price = trade['entry_price']
61
  stop_loss = trade.get('stop_loss')
62
  take_profit = trade.get('take_profit')
63
-
64
- should_close = False
65
- close_reason = ""
66
-
67
- if stop_loss and current_price <= stop_loss:
68
- should_close = True
69
- close_reason = f"Stop loss hit: {current_price} <= {stop_loss}"
70
- elif take_profit and current_price >= take_profit:
71
- should_close = True
72
- close_reason = f"Take profit hit: {current_price} >= {take_profit}"
73
-
74
  if not should_close and current_price > entry_price:
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')
86
-
87
  asyncio.create_task(run_bot_cycle_async())
88
-
89
- finally:
90
- r2_service_global.release_lock()
91
-
92
- if symbol in self.monitoring_tasks:
93
- del self.monitoring_tasks[symbol]
94
  break
95
-
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)
@@ -105,51 +77,26 @@ class RealTimeTradeMonitor:
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')
131
-
132
- should_halt_trading = False
133
- halt_reason = ""
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}")
@@ -159,36 +106,17 @@ async def monitor_market_async():
159
  async def validate_candidate_data_enhanced(candidate):
160
  try:
161
  required_fields = ['symbol', 'current_price', 'final_score', 'enhanced_final_score']
162
-
163
  for field in required_fields:
164
- if field not in candidate:
165
- candidate[field] = 0.0 if field.endswith('_score') or field == 'current_price' else 'UNKNOWN'
166
-
167
  candidate['current_price'] = safe_float_conversion(candidate.get('current_price'), 0.0)
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,
178
- 'general_whale_activity': {'sentiment': 'NEUTRAL', 'critical_alert': False}
179
- }
180
-
181
- if 'advanced_indicators' not in candidate:
182
- candidate['advanced_indicators'] = {}
183
-
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
@@ -197,341 +125,160 @@ 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
- }}
218
- """
219
-
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
- }
243
-
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",
251
- "risk_tolerance": 5,
252
- "optimal_scan_count": 100,
253
- }
254
 
255
  async def find_strategy_specific_candidates(strategy, scan_count):
256
  try:
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)
267
-
268
  processed_candidates = []
269
  for candidate in all_candidates[:30]:
270
  try:
271
  symbol_with_reasons = [{'symbol': candidate['symbol'], 'reasons': candidate.get('reasons', [])}]
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':
285
  strategy_candidates = []
286
  for candidate in processed_candidates:
287
  base_scores = candidate.get('base_strategy_scores', {})
288
  strategy_score = base_scores.get(strategy, 0)
289
-
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
345
-
346
  feature_processor = FeatureProcessor(updated_market_context, data_manager_global, learning_engine_global)
347
-
348
- processed_chunk = await asyncio.gather(*[
349
- feature_processor.process_and_score_symbol_enhanced(data) for data in chunk_data
350
- ])
351
  all_processed_candidates.extend([c for c in processed_chunk if c is not None])
352
-
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()
359
- if not updated_market_context:
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 {
406
- "symbol": candidate['symbol'],
407
- "decision": llm_analysis_data,
408
- "current_price": candidate['current_price'],
409
- "strategy": final_strategy
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
 
425
  async def re_analyze_open_trade_async(trade_data):
426
  symbol = trade_data.get('symbol')
427
-
428
  try:
429
  entry_time = datetime.fromisoformat(trade_data['entry_timestamp'])
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]
448
  try:
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 {
488
- "symbol": symbol,
489
- "decision": final_decision,
490
- "current_price": processed_data.get('current_price'),
491
- "hold_minutes": hold_minutes
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:
518
  if not trade.get('strategy') or trade['strategy'] == 'unknown':
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
527
-
528
  if open_trades:
529
  now = datetime.now()
530
- trades_to_reanalyze = [
531
- trade for trade in open_trades
532
- if now >= datetime.fromisoformat(trade.get('expected_target_time', now.isoformat()))
533
- ]
534
-
535
  if trades_to_reanalyze:
536
  for trade in trades_to_reanalyze:
537
  result = await re_analyze_open_trade_async(trade)
@@ -543,13 +290,10 @@ async def run_bot_cycle_async():
543
  trade_with_strategy['strategy'] = strategy
544
  await learning_engine_global.analyze_trade_outcome(trade_with_strategy, 'CLOSED_BY_REANALYSIS')
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)
@@ -558,80 +302,47 @@ async def run_bot_cycle_async():
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'],
570
- new_opportunity['decision'],
571
- new_opportunity['current_price']
572
- )
573
  newly_opened_trades = await r2_service_global.get_open_trades_async()
574
  for trade in newly_opened_trades:
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()
601
  llm_service_global = LLMService()
602
  contracts_database = await r2_service_global.load_contracts_db_async()
603
-
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:
634
- await cleanup_on_shutdown()
635
 
636
  application = FastAPI(lifespan=lifespan)
637
 
@@ -643,99 +354,65 @@ async def run_cycle_api():
643
  @application.get("/health")
644
  async def health_check():
645
  learning_metrics = {}
646
- if learning_engine_global and learning_engine_global.initialized:
647
- learning_metrics = await learning_engine_global.calculate_performance_metrics()
648
-
649
  api_stats = {}
650
- if data_manager_global:
651
- api_stats = data_manager_global.get_performance_stats()
652
-
653
  return {
654
- "status": "healthy",
655
- "timestamp": datetime.now().isoformat(),
656
- "services": {
657
  "r2_service": "initialized" if r2_service_global else "uninitialized",
658
  "llm_service": "initialized" if llm_service_global else "uninitialized",
659
  "data_manager": "initialized" if data_manager_global else "uninitialized",
660
  "learning_engine": "active" if learning_engine_global and learning_engine_global.initialized else "inactive",
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")
668
  async def get_performance_stats():
669
  try:
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:
678
- api_stats = data_manager_global.get_performance_stats()
679
-
680
  stats = {
681
- "timestamp": datetime.now().isoformat(),
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:
695
- raise HTTPException(status_code=500, detail=f"Failed to retrieve stats: {str(error)}")
696
 
697
  @application.get("/logs/status")
698
  async def get_logs_status():
699
  try:
700
  open_trades = await r2_service_global.get_open_trades_async()
701
  portfolio_state = await r2_service_global.get_portfolio_state_async()
702
-
703
  return {
704
- "logging_system": "active",
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())
@@ -744,5 +421,4 @@ def signal_handler(signum, frame):
744
  signal.signal(signal.SIGINT, signal_handler)
745
  signal.signal(signal.SIGTERM, signal_handler)
746
 
747
- if __name__ == "__main__":
748
- uvicorn.run(application, host="0.0.0.0", port=7860)
 
3
  from fastapi import FastAPI, HTTPException
4
  from datetime import datetime
5
  from r2 import R2Service
6
+ from LLM import LLMService
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, local_analyze_opportunity, local_re_analyze_trade
13
 
14
  r2_service_global = None
15
  data_manager_global = None
 
25
 
26
  async def start_monitoring(self):
27
  self.is_running = True
 
28
  while self.is_running:
29
  try:
30
  open_trades = await r2_service_global.get_open_trades_async()
 
31
  for trade in open_trades:
32
  symbol = trade['symbol']
33
  if symbol not in self.monitoring_tasks:
34
  asyncio.create_task(self._monitor_single_trade(trade))
35
  self.monitoring_tasks[symbol] = trade
 
36
  current_symbols = {trade['symbol'] for trade in open_trades}
37
  for symbol in list(self.monitoring_tasks.keys()):
38
+ if symbol not in current_symbols: del self.monitoring_tasks[symbol]
 
 
39
  await asyncio.sleep(10)
 
40
  except Exception as error:
41
  print(f"Real-time monitor error: {error}")
42
  await asyncio.sleep(30)
43
 
44
  async def _monitor_single_trade(self, trade):
45
  symbol = trade['symbol']
 
46
  while symbol in self.monitoring_tasks and self.is_running:
47
  try:
48
  current_price = await data_manager_global.get_latest_price_async(symbol)
49
+ if not current_price: await asyncio.sleep(15); continue
 
 
 
50
  entry_price = trade['entry_price']
51
  stop_loss = trade.get('stop_loss')
52
  take_profit = trade.get('take_profit')
53
+ should_close, close_reason = False, ""
54
+ if stop_loss and current_price <= stop_loss: should_close, close_reason = True, f"Stop loss hit: {current_price} <= {stop_loss}"
55
+ elif take_profit and current_price >= take_profit: should_close, close_reason = True, f"Take profit hit: {current_price} >= {take_profit}"
 
 
 
 
 
 
 
 
56
  if not should_close and current_price > entry_price:
57
  dynamic_stop = current_price * 0.98
58
+ if dynamic_stop > (stop_loss or 0): trade['stop_loss'] = dynamic_stop
 
 
59
  if should_close:
60
  if r2_service_global.acquire_lock():
61
  try:
62
  await r2_service_global.close_trade_async(trade, current_price)
 
63
  if learning_engine_global and learning_engine_global.initialized:
64
  await learning_engine_global.analyze_trade_outcome(trade, 'CLOSED_BY_MONITOR')
 
65
  asyncio.create_task(run_bot_cycle_async())
66
+ finally: r2_service_global.release_lock()
67
+ if symbol in self.monitoring_tasks: del self.monitoring_tasks[symbol]
 
 
 
 
68
  break
 
69
  await asyncio.sleep(15)
 
70
  except Exception as error:
71
  print(f"Real-time monitoring error for {symbol}: {error}")
72
  await asyncio.sleep(30)
 
77
 
78
  async def monitor_market_async():
79
  global data_manager_global, sentiment_analyzer_global
 
80
  init_attempts = 0
81
+ while data_manager_global is None and init_attempts < 10: await asyncio.sleep(3); init_attempts += 1
82
+ if data_manager_global is None: return
 
 
 
 
 
83
  while True:
84
  try:
85
  market_context = await sentiment_analyzer_global.get_market_sentiment()
86
+ if not market_context: state.MARKET_STATE_OK = True; await asyncio.sleep(60); continue
 
 
 
 
 
87
  whale_analysis = market_context.get('general_whale_activity', {})
88
  is_critical = whale_analysis.get('critical_alert', False)
 
89
  bitcoin_sentiment = market_context.get('btc_sentiment')
90
  fear_greed_index = market_context.get('fear_and_greed_index')
91
+ should_halt_trading, halt_reason = False, ""
92
+ if is_critical: should_halt_trading, halt_reason = True, f"CRITICAL whale activity detected"
93
+ elif bitcoin_sentiment == 'BEARISH' and (fear_greed_index is not None and fear_greed_index < 30): should_halt_trading, halt_reason = True, f"Bearish market conditions"
 
 
 
 
 
 
 
 
94
  if should_halt_trading:
95
  state.MARKET_STATE_OK = False
96
+ await r2_service_global.save_system_logs_async({"market_halt": True, "reason": halt_reason})
 
 
 
97
  else:
98
+ if not state.MARKET_STATE_OK: print("Market conditions improved. Resuming normal operations.")
 
99
  state.MARKET_STATE_OK = True
 
100
  await asyncio.sleep(60)
101
  except Exception as error:
102
  print(f"Error during market monitoring: {error}")
 
106
  async def validate_candidate_data_enhanced(candidate):
107
  try:
108
  required_fields = ['symbol', 'current_price', 'final_score', 'enhanced_final_score']
 
109
  for field in required_fields:
110
+ if field not in candidate: candidate[field] = 0.0 if field.endswith('_score') or field == 'current_price' else 'UNKNOWN'
 
 
111
  candidate['current_price'] = safe_float_conversion(candidate.get('current_price'), 0.0)
112
  candidate['final_score'] = safe_float_conversion(candidate.get('final_score'), 0.5)
113
  candidate['enhanced_final_score'] = safe_float_conversion(candidate.get('enhanced_final_score'), candidate['final_score'])
114
+ if 'reasons_for_candidacy' not in candidate: candidate['reasons_for_candidacy'] = ['unknown_reason']
115
+ if 'sentiment_data' not in candidate: candidate['sentiment_data'] = {'btc_sentiment': 'NEUTRAL','fear_and_greed_index': 50,'general_whale_activity': {'sentiment': 'NEUTRAL', 'critical_alert': False}}
116
+ if 'advanced_indicators' not in candidate: candidate['advanced_indicators'] = {}
117
+ if 'strategy_scores' not in candidate: candidate['strategy_scores'] = {}
118
+ if 'target_strategy' not in candidate: candidate['target_strategy'] = 'GENERIC'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  return True
 
120
  except Exception as error:
121
  print(f"Failed to validate candidate data for {candidate.get('symbol')}: {error}")
122
  return False
 
125
  try:
126
  whale_analysis = market_context.get('general_whale_activity', {})
127
  netflow_analysis = whale_analysis.get('netflow_analysis', {})
128
+ prompt = f"Analyze current market conditions and determine trading strategy.\n\nMarket Data:\n- BTC Sentiment: {market_context.get('btc_sentiment')}\n- Fear & Greed Index: {market_context.get('fear_and_greed_index')}\n- Whale Analysis: {whale_analysis.get('sentiment')}\n- Critical Alert: {whale_analysis.get('critical_alert')}\n- Net Flow: ${netflow_analysis.get('net_flow', 0):,.0f}\n\nOutput JSON:\n{{\"primary_strategy\": \"STRATEGY_NAME\",\"reasoning\": \"Brief reasoning\",\"risk_tolerance\": 5,\"optimal_scan_count\": 100}}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  response = await llm_service_global._call_llm(prompt)
 
130
  try:
131
  from helpers import parse_json_from_response
132
  json_str = parse_json_from_response(response)
133
  strategy_data = json.loads(json_str)
134
  except:
135
  net_flow = netflow_analysis.get('net_flow', 0)
136
+ if net_flow > 1000000: fallback_strategy = "AGGRESSIVE_GROWTH"
137
+ elif net_flow < -1000000: fallback_strategy = "CONSERVATIVE"
138
+ elif whale_analysis.get('critical_alert'): fallback_strategy = "CONSERVATIVE"
139
+ else: fallback_strategy = "GENERIC"
140
+ strategy_data = {"primary_strategy": fallback_strategy,"reasoning": "Fallback strategy","risk_tolerance": 5,"optimal_scan_count": 100}
 
 
 
 
 
 
 
 
 
 
 
141
  return strategy_data
 
142
  except Exception as error:
143
  print(f"Failed to analyze market strategy: {error}")
144
+ return {"primary_strategy": "GENERIC","reasoning": "Fallback due to analysis error","risk_tolerance": 5,"optimal_scan_count": 100}
 
 
 
 
 
145
 
146
  async def find_strategy_specific_candidates(strategy, scan_count):
147
  try:
148
  all_candidates = await data_manager_global.find_high_potential_candidates(scan_count * 2)
149
+ if not all_candidates: return []
 
 
 
150
  market_context = await data_manager_global.get_market_context_async()
151
+ if not market_context: return []
 
 
152
  feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
 
153
  processed_candidates = []
154
  for candidate in all_candidates[:30]:
155
  try:
156
  symbol_with_reasons = [{'symbol': candidate['symbol'], 'reasons': candidate.get('reasons', [])}]
157
  ohlcv_data = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
 
158
  if ohlcv_data and ohlcv_data[0]:
159
  processed = await feature_processor.process_and_score_symbol_enhanced(ohlcv_data[0])
160
+ if processed: processed_candidates.append(processed)
161
+ except Exception as e: print(f"Failed to process {candidate.get('symbol')}: {e}")
162
+ if not processed_candidates: return []
 
 
 
 
 
163
  if strategy != 'GENERIC':
164
  strategy_candidates = []
165
  for candidate in processed_candidates:
166
  base_scores = candidate.get('base_strategy_scores', {})
167
  strategy_score = base_scores.get(strategy, 0)
 
168
  if strategy_score > 0.2:
169
  candidate['strategy_match_score'] = strategy_score
170
  strategy_candidates.append(candidate)
171
+ sorted_candidates = sorted(strategy_candidates, key=lambda x: x.get('strategy_match_score', 0), reverse=True)
 
 
 
172
  top_candidates = sorted_candidates[:15]
173
  else:
174
+ sorted_candidates = sorted(processed_candidates, key=lambda x: x.get('enhanced_final_score', 0), reverse=True)
 
 
175
  top_candidates = sorted_candidates[:15]
 
176
  return top_candidates
 
177
  except Exception as error:
178
  print(f"Advanced filtering failed: {error}")
179
  return []
180
 
181
  async def find_new_opportunities_async():
182
  try:
183
+ await r2_service_global.save_system_logs_async({"opportunity_scan_started": True})
 
 
 
184
  market_context = await data_manager_global.get_market_context_async()
185
+ if not market_context: return
 
 
186
  strategy_decision = await analyze_market_strategy(market_context)
187
+ high_potential_candidates = await find_strategy_specific_candidates(strategy_decision['primary_strategy'], strategy_decision.get('optimal_scan_count', 100))
 
 
 
 
 
188
  if not high_potential_candidates:
189
  high_potential_candidates = await data_manager_global.find_high_potential_candidates(20)
190
  if high_potential_candidates:
191
+ for candidate in high_potential_candidates: candidate['target_strategy'] = 'GENERIC'
192
+ else: return
 
 
 
193
  all_processed_candidates = []
194
  CHUNK_SIZE = 5
 
195
  for index in range(0, len(high_potential_candidates), CHUNK_SIZE):
196
  chunk = high_potential_candidates[index:index+CHUNK_SIZE]
197
  chunk_data = await data_manager_global.get_fast_pass_data_async(chunk)
 
198
  updated_market_context = await data_manager_global.get_market_context_async()
199
+ if not updated_market_context: updated_market_context = market_context
 
 
200
  feature_processor = FeatureProcessor(updated_market_context, data_manager_global, learning_engine_global)
201
+ processed_chunk = await asyncio.gather(*[feature_processor.process_and_score_symbol_enhanced(data) for data in chunk_data])
 
 
 
202
  all_processed_candidates.extend([c for c in processed_chunk if c is not None])
 
203
  await asyncio.sleep(1)
204
+ if not all_processed_candidates: return
 
 
 
205
  updated_market_context = await data_manager_global.get_market_context_async()
206
+ if not updated_market_context: updated_market_context = market_context
 
 
207
  feature_processor = FeatureProcessor(updated_market_context, data_manager_global, learning_engine_global)
208
  OPPORTUNITY_COUNT = 10
209
  top_candidates = feature_processor.filter_top_candidates(all_processed_candidates, OPPORTUNITY_COUNT)
210
+ await r2_service_global.save_candidates_data_async(candidates_data=top_candidates, reanalysis_data={"strategy_used": strategy_decision, "market_conditions": market_context})
211
+ if not top_candidates: return
 
 
 
 
 
 
 
 
 
 
212
  for candidate in top_candidates:
213
  try:
214
+ if not await validate_candidate_data_enhanced(candidate): continue
 
 
215
  llm_analysis_data = await llm_service_global.get_trading_decision(candidate)
216
+ if not llm_analysis_data: continue
217
+ if llm_analysis_data.get('action') == "HOLD": continue
 
 
 
 
 
218
  if llm_analysis_data.get('action') in ["BUY", "SELL"]:
219
  final_strategy = llm_analysis_data.get('strategy')
220
  candidate_strategy = candidate.get('target_strategy', 'GENERIC')
221
+ if not final_strategy or final_strategy == 'unknown': final_strategy = candidate_strategy; llm_analysis_data['strategy'] = final_strategy
222
+ await r2_service_global.save_system_logs_async({"new_opportunity_found": True, "symbol": candidate['symbol'],"action": llm_analysis_data.get('action'), "strategy": final_strategy})
223
+ return {"symbol": candidate['symbol'],"decision": llm_analysis_data,"current_price": candidate['current_price'],"strategy": final_strategy}
224
+ except Exception as error: print(f"LLM error for {candidate.get('symbol', 'unknown')}: {error}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  return None
 
226
  except Exception as error:
227
  print(f"Error while scanning for opportunities: {error}")
228
+ await r2_service_global.save_system_logs_async({"opportunity_scan_error": True, "error": str(error)})
 
 
 
229
  return None
230
 
231
  async def re_analyze_open_trade_async(trade_data):
232
  symbol = trade_data.get('symbol')
 
233
  try:
234
  entry_time = datetime.fromisoformat(trade_data['entry_timestamp'])
235
  current_time = datetime.now()
236
  hold_minutes = (current_time - entry_time).total_seconds() / 60
 
237
  original_strategy = trade_data.get('strategy')
238
+ if not original_strategy or original_strategy == 'unknown': original_strategy = trade_data.get('decision_data', {}).get('strategy', 'GENERIC')
239
+ try: market_context = await data_manager_global.get_market_context_async()
240
+ except Exception: market_context = {'btc_sentiment': 'NEUTRAL'}
 
 
 
 
 
241
  symbol_with_reasons = [{'symbol': symbol, 'reasons': ['re-analysis']}]
242
  ohlcv_data_list = await data_manager_global.get_fast_pass_data_async(symbol_with_reasons)
243
+ if not ohlcv_data_list: return None
 
 
244
  raw_data = ohlcv_data_list[0]
245
  try:
246
  updated_market_context = await data_manager_global.get_market_context_async()
247
+ if updated_market_context: market_context = updated_market_context
248
+ except Exception: pass
 
 
 
249
  feature_processor = FeatureProcessor(market_context, data_manager_global, learning_engine_global)
250
  processed_data = await feature_processor.process_and_score_symbol(raw_data)
251
+ if not processed_data: return None
252
+ await r2_service_global.save_candidates_data_async(candidates_data=None, reanalysis_data={'market_context': market_context, 'processed_data': processed_data})
253
+ try: re_analysis_decision = await llm_service_global.re_analyze_trade_async(trade_data, processed_data)
254
+ except Exception: re_analysis_decision = local_re_analyze_trade(trade_data, processed_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  final_decision = _apply_patience_logic(re_analysis_decision, hold_minutes, trade_data, processed_data)
256
+ if not final_decision.get('strategy'): final_decision['strategy'] = original_strategy
257
+ await r2_service_global.save_system_logs_async({"trade_reanalyzed": True, "symbol": symbol, "action": final_decision.get('action'),"hold_minutes": hold_minutes, "strategy": final_decision.get('strategy')})
258
+ return {"symbol": symbol, "decision": final_decision,"current_price": processed_data.get('current_price'), "hold_minutes": hold_minutes}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  except Exception as error:
260
  print(f"Error during trade re-analysis: {error}")
261
+ await r2_service_global.save_system_logs_async({"reanalysis_error": True, "symbol": symbol, "error": str(error)})
 
 
 
 
262
  return None
263
 
264
  async def run_bot_cycle_async():
265
  try:
266
+ await r2_service_global.save_system_logs_async({"cycle_started": True})
267
+ if not r2_service_global.acquire_lock(): return
 
 
 
 
 
268
  open_trades = []
269
  try:
270
  open_trades = await r2_service_global.get_open_trades_async()
 
271
  trades_fixed = 0
272
  for trade in open_trades:
273
  if not trade.get('strategy') or trade['strategy'] == 'unknown':
274
  original_strategy = trade.get('decision_data', {}).get('strategy', 'GENERIC')
275
  trade['strategy'] = original_strategy
276
  trades_fixed += 1
277
+ if trades_fixed > 0: await r2_service_global.save_open_trades_async(open_trades)
 
 
 
278
  should_look_for_new_trade = not open_trades
 
279
  if open_trades:
280
  now = datetime.now()
281
+ trades_to_reanalyze = [trade for trade in open_trades if now >= datetime.fromisoformat(trade.get('expected_target_time', now.isoformat()))]
 
 
 
 
282
  if trades_to_reanalyze:
283
  for trade in trades_to_reanalyze:
284
  result = await re_analyze_open_trade_async(trade)
 
290
  trade_with_strategy['strategy'] = strategy
291
  await learning_engine_global.analyze_trade_outcome(trade_with_strategy, 'CLOSED_BY_REANALYSIS')
292
  should_look_for_new_trade = True
293
+ elif result and result['decision'].get('action') == "UPDATE_TRADE": await r2_service_global.update_trade_async(trade, result['decision'])
 
 
294
  if should_look_for_new_trade:
295
  portfolio_state = await r2_service_global.get_portfolio_state_async()
296
  current_capital = portfolio_state.get("current_capital_usd", 0)
 
297
  if current_capital <= 0:
298
  if len(open_trades) == 0:
299
  initial_capital = portfolio_state.get("initial_capital_usd", 10.0)
 
302
  portfolio_state["invested_capital_usd"] = 0.0
303
  await r2_service_global.save_portfolio_state_async(portfolio_state)
304
  current_capital = initial_capital
 
305
  if current_capital > 1:
306
  new_opportunity = await find_new_opportunities_async()
307
  if new_opportunity:
308
+ if not new_opportunity['decision'].get('strategy'): new_opportunity['decision']['strategy'] = new_opportunity.get('strategy', 'GENERIC')
309
+ await r2_service_global.save_new_trade_async(new_opportunity['symbol'], new_opportunity['decision'], new_opportunity['current_price'])
 
 
 
 
 
 
310
  newly_opened_trades = await r2_service_global.get_open_trades_async()
311
  for trade in newly_opened_trades:
312
  if trade['symbol'] == new_opportunity['symbol']:
313
  asyncio.create_task(realtime_monitor._monitor_single_trade(trade))
314
  break
 
315
  finally:
316
  r2_service_global.release_lock()
317
+ await r2_service_global.save_system_logs_async({"cycle_completed": True, "open_trades": len(open_trades)})
 
 
 
 
318
  except Exception as error:
319
  print(f"Unhandled error in main cycle: {error}")
320
+ await r2_service_global.save_system_logs_async({"cycle_error": True, "error": str(error)})
321
+ if r2_service_global.lock_acquired: r2_service_global.release_lock()
 
 
 
 
322
 
323
  @asynccontextmanager
324
  async def lifespan(application: FastAPI):
325
  global r2_service_global, data_manager_global, llm_service_global, learning_engine_global, realtime_monitor, sentiment_analyzer_global
 
326
  try:
327
  r2_service_global = R2Service()
328
  llm_service_global = LLMService()
329
  contracts_database = await r2_service_global.load_contracts_db_async()
 
330
  data_manager_global = DataManager(contracts_database)
331
  await data_manager_global.initialize()
 
332
  sentiment_analyzer_global = SentimentAnalyzer(data_manager_global)
 
333
  learning_engine_global = LearningEngine(r2_service_global, data_manager_global)
334
  await learning_engine_global.initialize_enhanced()
 
335
  await learning_engine_global.force_strategy_learning()
 
336
  realtime_monitor = RealTimeTradeMonitor()
 
337
  asyncio.create_task(monitor_market_async())
338
  asyncio.create_task(realtime_monitor.start_monitoring())
339
+ await r2_service_global.save_system_logs_async({"application_started": True})
 
 
 
 
340
  yield
 
341
  except Exception as error:
342
  print(f"Application startup failed: {error}")
343
+ if r2_service_global: await r2_service_global.save_system_logs_async({"application_startup_failed": True, "error": str(error)})
 
 
 
 
344
  raise
345
+ finally: await cleanup_on_shutdown()
 
346
 
347
  application = FastAPI(lifespan=lifespan)
348
 
 
354
  @application.get("/health")
355
  async def health_check():
356
  learning_metrics = {}
357
+ if learning_engine_global and learning_engine_global.initialized: learning_metrics = await learning_engine_global.calculate_performance_metrics()
 
 
358
  api_stats = {}
359
+ if data_manager_global: api_stats = data_manager_global.get_performance_stats()
 
 
360
  return {
361
+ "status": "healthy", "timestamp": datetime.now().isoformat(), "services": {
 
 
362
  "r2_service": "initialized" if r2_service_global else "uninitialized",
363
  "llm_service": "initialized" if llm_service_global else "uninitialized",
364
  "data_manager": "initialized" if data_manager_global else "uninitialized",
365
  "learning_engine": "active" if learning_engine_global and learning_engine_global.initialized else "inactive",
366
  "realtime_monitor": "running" if realtime_monitor and realtime_monitor.is_running else "stopped"
367
+ }, "market_state_ok": state.MARKET_STATE_OK, "learning_engine": learning_metrics
 
 
368
  }
369
 
370
  @application.get("/stats")
371
  async def get_performance_stats():
372
  try:
373
  market_context = await data_manager_global.get_market_context_async() if data_manager_global else {}
 
374
  learning_stats = {}
375
+ if learning_engine_global and learning_engine_global.initialized: learning_stats = await learning_engine_global.calculate_performance_metrics()
 
 
376
  api_stats = {}
377
+ if data_manager_global: api_stats = data_manager_global.get_performance_stats()
 
 
378
  stats = {
379
+ "timestamp": datetime.now().isoformat(), "data_manager": api_stats, "market_state": {
380
+ "is_healthy": state.MARKET_STATE_OK, "context": market_context
381
+ }, "realtime_monitoring": {
 
 
 
 
382
  "active_trades": len(realtime_monitor.monitoring_tasks) if realtime_monitor else 0,
383
  "is_running": realtime_monitor.is_running if realtime_monitor else False
384
+ }, "learning_engine": learning_stats
 
385
  }
386
  return stats
387
+ except Exception as error: raise HTTPException(status_code=500, detail=f"Failed to retrieve stats: {str(error)}")
 
388
 
389
  @application.get("/logs/status")
390
  async def get_logs_status():
391
  try:
392
  open_trades = await r2_service_global.get_open_trades_async()
393
  portfolio_state = await r2_service_global.get_portfolio_state_async()
 
394
  return {
395
+ "logging_system": "active", "open_trades_count": len(open_trades),
 
396
  "current_capital": portfolio_state.get("current_capital_usd", 0),
397
  "total_trades": portfolio_state.get("total_trades", 0),
398
  "timestamp": datetime.now().isoformat()
399
  }
400
+ except Exception as error: raise HTTPException(status_code=500, detail=f"Failed to get logs status: {str(error)}")
 
401
 
402
  async def cleanup_on_shutdown():
403
  global r2_service_global, data_manager_global, realtime_monitor, learning_engine_global
404
  print("Shutdown signal received. Cleaning up...")
 
405
  if r2_service_global:
406
+ try: await r2_service_global.save_system_logs_async({"application_shutdown": True})
407
+ except Exception: pass
 
 
 
 
 
408
  if learning_engine_global and learning_engine_global.initialized:
409
  try:
410
  await learning_engine_global.save_weights_to_r2()
411
  await learning_engine_global.save_performance_history()
412
+ except Exception: pass
413
+ if realtime_monitor: realtime_monitor.stop_monitoring()
414
+ if r2_service_global and r2_service_global.lock_acquired: r2_service_global.release_lock()
415
+ if data_manager_global: await data_manager_global.close()
 
 
 
 
 
 
416
 
417
  def signal_handler(signum, frame):
418
  asyncio.create_task(cleanup_on_shutdown())
 
421
  signal.signal(signal.SIGINT, signal_handler)
422
  signal.signal(signal.SIGTERM, signal_handler)
423
 
424
+ if __name__ == "__main__": uvicorn.run(application, host="0.0.0.0", port=7860)