Riy777 commited on
Commit
b8afcfc
·
verified ·
1 Parent(s): a903961

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -778
app.py DELETED
@@ -1,778 +0,0 @@
1
- # ==============================================================================
2
- # 🚀 app.py (V28.4 - GEM-Architect: The Original Heavyweight + Live Logs)
3
- # Architecture: Staged Filtering -> Top 10 Deep Dive -> Oracle -> Sniper -> Hydra.
4
- # Features:
5
- # 1. Full original logic preserved (Timers, Charts, Logic).
6
- # 2. Added LogRedirector to visualize "Raw vs Calib" prints in UI.
7
- # 3. Dynamic Scoring for Whales, News, and Advanced MC included.
8
- # ==============================================================================
9
-
10
- import os
11
- import sys
12
- import traceback
13
- import asyncio
14
- import gc
15
- import time
16
- import json
17
- import logging
18
- from collections import deque # For Log Buffer
19
- from datetime import datetime, timedelta
20
- from contextlib import asynccontextmanager, redirect_stdout, redirect_stderr
21
- from io import StringIO
22
- from typing import List, Dict, Any, Optional
23
-
24
- # المكتبات الخارجية للواجهة والخادم
25
- from fastapi import FastAPI, HTTPException, BackgroundTasks
26
- import gradio as gr
27
- import pandas as pd
28
- import plotly.graph_objects as go
29
-
30
- # ==============================================================================
31
- # 📝 إعداد نظام السجلات الحي (Live Terminal Logging)
32
- # ==============================================================================
33
- # نحتفظ بآخر 500 سطر لضمان عدم فقدان السياق
34
- LOG_BUFFER = deque(maxlen=500)
35
-
36
- class LogRedirector:
37
- """
38
- يقوم بالتقاط كل ما يطبعه النظام (print) ويرسله إلى الواجهة
39
- مع الاحتفاظ بنسخة في الـ Console الخلفية.
40
- """
41
- def __init__(self, original_stream):
42
- self.original_stream = original_stream
43
-
44
- def write(self, message):
45
- self.original_stream.write(message)
46
- self.original_stream.flush()
47
- if message and message.strip():
48
- LOG_BUFFER.append(message.rstrip())
49
-
50
- def flush(self):
51
- self.original_stream.flush()
52
-
53
- # تفعيل التحويل
54
- sys.stdout = LogRedirector(sys.stdout)
55
- sys.stderr = LogRedirector(sys.stderr)
56
-
57
- logging.basicConfig(
58
- level=logging.INFO,
59
- format="[%(levelname)s] %(message)s",
60
- handlers=[logging.StreamHandler(sys.stdout)]
61
- )
62
- logger = logging.getLogger("TitanCore")
63
-
64
- # ==============================================================================
65
- # 📥 استيراد الوحدات الأساسية (Module Imports)
66
- # ==============================================================================
67
- try:
68
- # Core Data & ML Modules
69
- from r2 import R2Service
70
- from ml_engine.data_manager import DataManager
71
- from ml_engine.processor import MLProcessor
72
-
73
- # Whale & Network Analysis Modules
74
- from whale_monitor.core import EnhancedWhaleMonitor
75
- from whale_monitor.rpc_manager import AdaptiveRpcManager
76
-
77
- # Sentiment & News Modules
78
- from sentiment_news import NewsFetcher
79
- from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
80
-
81
- # Learning & Trading Execution Modules
82
- from learning_hub.hub_manager import LearningHubManager
83
- from trade_manager import TradeManager
84
- except ImportError as e:
85
- logger.critical(f"❌ [FATAL ERROR] Failed to import core modules: {e}")
86
- traceback.print_exc()
87
- # sys.exit(1) # Disabled to allow UI to show error
88
-
89
- # ==============================================================================
90
- # 🌐 المتغيرات العامة (Global Context & Singletons)
91
- # ==============================================================================
92
- r2: R2Service = None
93
- data_manager: DataManager = None
94
- ml_processor: MLProcessor = None
95
- hub_manager: LearningHubManager = None
96
- trade_manager: TradeManager = None
97
- whale_monitor: EnhancedWhaleMonitor = None
98
- news_fetcher: NewsFetcher = None
99
- senti_analyzer: SentimentIntensityAnalyzer = None
100
- sys_state: 'SystemState' = None
101
-
102
- # ==============================================================================
103
- # 🔄 حالة النظام (State Management System)
104
- # ==============================================================================
105
- class SystemState:
106
- """
107
- Holds the runtime state of the application.
108
- Manages cycle flags, errors, status messages, and timestamps.
109
- """
110
- def __init__(self):
111
- self.ready = False
112
- self.cycle_running = False
113
- self.training_running = False
114
- self.auto_pilot = True
115
-
116
- # Timestamps & Logs
117
- self.last_cycle_time: datetime = None
118
- self.last_cycle_error = None
119
- self.app_start_time = datetime.now()
120
-
121
- # Status Messages for UI
122
- self.last_cycle_logs = "System Initializing..."
123
- self.training_status_msg = "Waiting for data..."
124
-
125
- # Configuration
126
- self.scan_interval = 60 # Seconds (5 mins default)
127
-
128
- def set_ready(self):
129
- """Mark system as fully initialized and ready."""
130
- self.ready = True
131
- self.last_cycle_logs = "✅ System Ready. Auto-Pilot ON."
132
- logger.info("✅ System State set to READY.")
133
-
134
- def set_cycle_start(self):
135
- """Mark the start of a scanning cycle."""
136
- self.cycle_running = True
137
- self.last_cycle_logs = "🌀 [Cycle START] Scanning Markets..."
138
- logger.info("🌀 Cycle STARTED.")
139
-
140
- def set_cycle_end(self, error=None, logs=None):
141
- """Mark the end of a scanning cycle and store results."""
142
- self.cycle_running = False
143
- self.last_cycle_time = datetime.now()
144
- self.last_cycle_error = str(error) if error else None
145
-
146
- if logs:
147
- self.last_cycle_logs = logs
148
- elif error:
149
- self.last_cycle_logs = f"❌ [Cycle ERROR] {error}"
150
- logger.error(f"Cycle Error: {error}")
151
- else:
152
- self.last_cycle_logs = f"✅ [Cycle END] Finished successfully."
153
- logger.info("✅ Cycle ENDED.")
154
-
155
- # Initialize State Instance
156
- sys_state = SystemState()
157
-
158
- # ==============================================================================
159
- # 🛠️ دوال مساعدة (Utilities & Formatting)
160
- # ==============================================================================
161
- def format_crypto_price(price):
162
- """Format price string nicely for UI (removing trailing zeros)."""
163
- if price is None: return "0.0"
164
- try:
165
- p = float(price)
166
- if p == 0: return "0.0"
167
- return "{:.8f}".format(p).rstrip('0').rstrip('.')
168
- except: return str(price)
169
-
170
- def calculate_duration_str(timestamp_str):
171
- """Calculate duration string (Days/Hours/Mins) from a timestamp."""
172
- if not timestamp_str: return "--:--:--"
173
- try:
174
- if isinstance(timestamp_str, str):
175
- start_time = datetime.fromisoformat(timestamp_str)
176
- else: start_time = timestamp_str
177
-
178
- diff = datetime.now() - start_time
179
- total_seconds = int(diff.total_seconds())
180
-
181
- days = total_seconds // 86400
182
- hours = (total_seconds % 86400) // 3600
183
- minutes = (total_seconds % 3600) // 60
184
- seconds = total_seconds % 60
185
-
186
- if days > 0: return f"{days}d {hours:02}:{minutes:02}:{seconds:02}"
187
- return f"{hours:02}:{minutes:02}:{seconds:02}"
188
- except: return "--:--:--"
189
-
190
- # ==============================================================================
191
- # 🤖 Auto-Pilot Daemon (Background Service)
192
- # ==============================================================================
193
- async def auto_pilot_loop():
194
- """
195
- Background task that manages scanning AND monitoring.
196
- """
197
- logger.info("🤖 [Auto-Pilot] Daemon started (Monitoring Mode Enabled).")
198
- while True:
199
- try:
200
- await asyncio.sleep(5) # Check every 5 seconds (Faster response)
201
-
202
- if not sys_state.ready: continue
203
-
204
- # 1. حالة التدريب
205
- if hub_manager and int(time.time()) % 60 == 0:
206
- is_ready, msg = await hub_manager.check_training_readiness()
207
- sys_state.training_status_msg = msg
208
-
209
- # 2. فحص حالة الصفقات المفتوحة (Watchdog Check)
210
- if trade_manager and len(trade_manager.open_positions) > 0:
211
- # التأكد من عمل الحلقات الخلفية
212
- wd_status = await trade_manager.ensure_active_guardians()
213
- # LogRedirector handles logging
214
- continue
215
-
216
- # 3. Trigger Cycle (فقط إذا لم تكن هناك صفقات)
217
- if sys_state.auto_pilot and not sys_state.cycle_running and not sys_state.training_running:
218
- # Check time since last cycle
219
- if sys_state.last_cycle_time:
220
- elapsed = (datetime.now() - sys_state.last_cycle_time).total_seconds()
221
- if elapsed < sys_state.scan_interval:
222
- continue
223
-
224
- logger.info("🤖 [Auto-Pilot] Triggering scan...")
225
- asyncio.create_task(run_unified_cycle())
226
- await asyncio.sleep(5)
227
-
228
- except Exception as e:
229
- logger.error(f"⚠️ [Auto-Pilot Error] {e}")
230
- await asyncio.sleep(30)
231
-
232
- # ==============================================================================
233
- # 🚀 Lifespan (Server Startup/Shutdown & Injection)
234
- # ==============================================================================
235
- @asynccontextmanager
236
- async def lifespan(app: FastAPI):
237
- """
238
- Application Lifecycle Manager.
239
- Initializes all services, injects dependencies, and handles shutdown.
240
- """
241
- global r2, data_manager, ml_processor, hub_manager, trade_manager, whale_monitor, news_fetcher, senti_analyzer, sys_state
242
-
243
- logger.info("\n🚀 [System] Startup Sequence (Titan V28.4)...")
244
- try:
245
- # 1. Initialize R2 & Data Manager
246
- logger.info(" [1/7] Initializing R2 & Data Services...")
247
- r2 = R2Service()
248
- data_manager = DataManager(contracts_db={}, whale_monitor=None, r2_service=r2)
249
- await data_manager.initialize()
250
- await data_manager.load_contracts_from_r2()
251
-
252
- # 2. Initialize Auxiliary Services (Whale & News)
253
- logger.info(" [2/7] Starting Auxiliary Services (Whale & News)...")
254
- whale_monitor = EnhancedWhaleMonitor(contracts_db=data_manager.get_contracts_db(), r2_service=r2)
255
- rpc_mgr = AdaptiveRpcManager(data_manager.http_client)
256
- whale_monitor.set_rpc_manager(rpc_mgr)
257
- logger.info(" ✅ [System] RPC Manager injected into Whale Monitor.")
258
-
259
- news_fetcher = NewsFetcher()
260
- senti_analyzer = SentimentIntensityAnalyzer()
261
- data_manager.whale_monitor = whale_monitor
262
-
263
- # 3. Initialize Learning Hub
264
- logger.info(" [3/7] Initializing Learning Hub...")
265
- hub_manager = LearningHubManager(r2_service=r2, data_manager=data_manager)
266
- await hub_manager.initialize()
267
-
268
- # 4. Initialize ML Processor (Oracle V4 Integration)
269
- logger.info(" [4/7] Initializing Central ML Processor...")
270
- ml_processor = MLProcessor(data_manager=data_manager)
271
- await ml_processor.initialize()
272
- ml_processor.hub_manager = hub_manager
273
-
274
- # 5. Initialize Trade Manager (The Hand)
275
- logger.info(" [5/7] Initializing Trade Manager...")
276
- trade_manager = TradeManager(r2_service=r2, data_manager=data_manager, processor=ml_processor)
277
- trade_manager.learning_hub = hub_manager
278
- await trade_manager.initialize_sentry_exchanges()
279
-
280
- # 6. Start Background Loops
281
- logger.info(" [6/7] Restarting Active Sentries...")
282
- await trade_manager.start_sentry_loops()
283
-
284
- # 7. Finalize Startup
285
- logger.info(" [7/7] Launching Auto-Pilot...")
286
- sys_state.set_ready()
287
- asyncio.create_task(auto_pilot_loop())
288
- logger.info("✅ [System READY] All modules operational.")
289
- yield
290
-
291
- except Exception as e:
292
- logger.critical(f"❌ [FATAL STARTUP ERROR] {e}")
293
- traceback.print_exc()
294
- finally:
295
- logger.info("\n🛑 [System] Shutdown Sequence...")
296
- sys_state.ready = False
297
-
298
- if hub_manager: await hub_manager.shutdown()
299
- if trade_manager: await trade_manager.stop_sentry_loops()
300
- if data_manager: await data_manager.close()
301
-
302
- if whale_monitor and whale_monitor.rpc_manager:
303
- await whale_monitor.rpc_manager.close()
304
-
305
- logger.info("✅ [System] Shutdown Complete.")
306
-
307
- # ==============================================================================
308
- # 🧠 Helper Tasks (Layer 2 Calculation)
309
- # ==============================================================================
310
- async def _analyze_symbol_task(symbol: str) -> Dict[str, Any]:
311
- """
312
- Task to analyze a single symbol.
313
- """
314
- try:
315
- required_tfs = ["5m", "15m", "1h", "4h"]
316
-
317
- # Fetch 300 candles to satisfy Pattern Engine's 200 candle requirement
318
- data_tasks = [data_manager.get_latest_ohlcv(symbol, tf, limit=300) for tf in required_tfs]
319
- all_data = await asyncio.gather(*data_tasks)
320
-
321
- ohlcv_data = {}
322
- for tf, data in zip(required_tfs, all_data):
323
- if data and len(data) > 0: ohlcv_data[tf] = data
324
-
325
- if '1h' not in ohlcv_data or '5m' not in ohlcv_data:
326
- return None
327
-
328
- current_price = await data_manager.get_latest_price_async(symbol)
329
- raw_data = {'symbol': symbol, 'ohlcv': ohlcv_data, 'current_price': current_price, 'timestamp': time.time()}
330
-
331
- # Run Processor
332
- res = await ml_processor.process_compound_signal(raw_data)
333
- if not res: return None
334
-
335
- titan_s = res.get('titan_score', res.get('components', {}).get('titan_score', 0.5))
336
- pattern_s = res.get('patterns_score', res.get('components', {}).get('patterns_score', 0.5))
337
- mc_light_s = res.get('mc_score', res.get('components', {}).get('mc_score', 0.5))
338
-
339
- l2_base_score = (titan_s * 0.50) + (pattern_s * 0.40) + (mc_light_s * 0.10)
340
-
341
- res['enhanced_final_score'] = l2_base_score
342
- res['l2_base_score'] = l2_base_score
343
- res['titan_score'] = titan_s
344
- res['patterns_score'] = pattern_s
345
- res['mc_score'] = mc_light_s
346
-
347
- return res
348
-
349
- except Exception: return None
350
-
351
- # ==============================================================================
352
- # 🚀 Unified Smart Cycle (Oracle V4 Logic + Deep Dive Top 10)
353
- # ==============================================================================
354
- async def run_unified_cycle():
355
- """
356
- Full Analysis Cycle with Strict Scoring & Re-Ranking.
357
- """
358
- if sys_state.cycle_running or sys_state.training_running: return
359
- if not sys_state.ready: return
360
-
361
- sys_state.set_cycle_start()
362
-
363
- try:
364
- # --- LAYER 0: Guardian ---
365
- await trade_manager.sync_internal_state_with_r2()
366
- if len(trade_manager.open_positions) > 0:
367
- symbol = list(trade_manager.open_positions.keys())[0]
368
- trade = trade_manager.open_positions[symbol]
369
- logger.info(f"🔒 [Guardian] Monitoring active trade: {symbol}")
370
- curr_p = await data_manager.get_latest_price_async(symbol)
371
- entry_p = float(trade.get('entry_price', 0))
372
- if entry_p > 0:
373
- pnl = ((curr_p - entry_p) / entry_p) * 100
374
- logger.info(f" 📊 PnL: {pnl:+.2f}% | TP: {trade.get('tp_price')} | SL: {trade.get('sl_price')}")
375
- sys_state.set_cycle_end()
376
- return
377
-
378
- # --- LAYER 1: Screening ---
379
- logger.info(" [1/5] 🔍 L1 Screening (Liquidity & Volume)...")
380
- candidates = await data_manager.layer1_rapid_screening()
381
- if not candidates:
382
- logger.info("⚠️ No L1 candidates found.")
383
- sys_state.set_cycle_end()
384
- return
385
-
386
- # --- LAYER 2: Deep Analysis ---
387
- logger.info(f" [2/5] 🧠 L2 Deep Analysis ({len(candidates)} items)...")
388
- tasks = [_analyze_symbol_task(c['symbol']) for c in candidates]
389
- results = await asyncio.gather(*tasks)
390
- valid_l2 = [res for res in results if res is not None]
391
-
392
- semi_finalists = sorted(valid_l2, key=lambda x: x.get('l2_base_score', 0.0), reverse=True)[:10]
393
-
394
- if not semi_finalists:
395
- logger.info("⚠️ No valid L2 candidates.")
396
- sys_state.set_cycle_end()
397
- return
398
-
399
- # --- LAYER 3: Deep Dive ---
400
- logger.info(f" [3/5] 📡 L3 Deep Dive (Dynamic Scoring) for TOP {len(semi_finalists)}...")
401
-
402
- final_candidates = []
403
-
404
- for sig in semi_finalists:
405
- symbol = sig['symbol']
406
- l2_score = sig.get('l2_base_score', 0.0)
407
-
408
- # A. Whale Analysis
409
- whale_points = 0.0
410
- try:
411
- if whale_monitor:
412
- w_data = await whale_monitor.get_symbol_whale_activity(symbol, known_price=sig.get('current_price', 0))
413
- if w_data and w_data.get('data_available', False) and 'trading_signal' in w_data:
414
- signal = w_data['trading_signal']
415
- action = signal.get('action', 'HOLD')
416
- confidence = float(signal.get('confidence', 0.5))
417
- dynamic_impact = 0.10 * confidence
418
-
419
- if action == 'BUY': whale_points = dynamic_impact
420
- elif action == 'SELL': whale_points = -dynamic_impact
421
- except Exception: pass
422
-
423
- # B. News Analysis
424
- news_points = 0.0
425
- try:
426
- if news_fetcher and senti_analyzer:
427
- n_data = await news_fetcher.get_news(symbol)
428
- summary_text = n_data.get('summary', '')
429
- if "No specific news" not in summary_text:
430
- sent = senti_analyzer.polarity_scores(summary_text)
431
- news_points = sent['compound'] * 0.05
432
- except Exception: pass
433
-
434
- # C. Advanced Monte Carlo
435
- mc_a_points = 0.0
436
- try:
437
- mc_a_points = await ml_processor.run_advanced_monte_carlo(symbol, '1h')
438
- except Exception: pass
439
-
440
- final_score = l2_score + whale_points + news_points + mc_a_points
441
-
442
- sig['whale_score'] = whale_points
443
- sig['news_score'] = news_points
444
- sig['mc_advanced_score'] = mc_a_points
445
- sig['final_total_score'] = final_score
446
-
447
- final_candidates.append(sig)
448
-
449
- # --- RE-RANKING ---
450
- final_candidates.sort(key=lambda x: x['final_total_score'], reverse=True)
451
-
452
- approved_signals = []
453
-
454
- header = (f"{'SYM':<9} | {'TITAN':<5} | {'MC(L)':<5} | {'PATT':<5} | "
455
- f"{'WHALE':<6} | {'NEWS':<6} | {'MC(A)':<6} | {'FINAL':<6} | {'ORACLE':<6} | {'STATUS'}")
456
- logger.info("-" * 110)
457
- logger.info(header)
458
- logger.info("-" * 110)
459
-
460
- for sig in final_candidates:
461
- symbol = sig['symbol']
462
- decision = await ml_processor.consult_oracle(sig)
463
-
464
- action = decision.get('action', 'WAIT')
465
- oracle_conf = decision.get('confidence', 0.0)
466
- target_class = decision.get('target_class', '')
467
-
468
- status_str = "WAIT 🔴"
469
- if action == 'WATCH' or action == 'BUY':
470
- status_str = f"✅ {target_class}"
471
- sig.update(decision)
472
- approved_signals.append(sig)
473
-
474
- logger.info(
475
- f"{symbol:<9} | "
476
- f"{sig.get('titan_score',0):.2f} | "
477
- f"{sig.get('mc_score',0):.2f} | "
478
- f"{sig.get('patterns_score',0):.2f} | "
479
- f"{sig.get('whale_score',0):+.2f} | "
480
- f"{sig.get('news_score',0):+.2f} | "
481
- f"{sig.get('mc_advanced_score',0):+.2f} | "
482
- f"{sig.get('final_total_score',0):.2f} | "
483
- f"{oracle_conf:.2f} | "
484
- f"{status_str}"
485
- )
486
-
487
- # --- LAYER 4: Sniper Execution ---
488
- if approved_signals:
489
- logger.info("-" * 110)
490
- logger.info(f" [4/5] 🎯 L4 Sniper Execution ({len(approved_signals)} candidates)...")
491
- await trade_manager.select_and_execute_best_signal(approved_signals)
492
- else:
493
- logger.info(" -> 🛑 No candidates approved by Oracle for Sniper check.")
494
-
495
- gc.collect()
496
- sys_state.set_cycle_end()
497
-
498
- except Exception as e:
499
- logger.error(f"❌ [Cycle ERROR] {e}")
500
- traceback.print_exc()
501
- sys_state.set_cycle_end(error=e)
502
-
503
- # ==============================================================================
504
- # 🎓 Training & Helper Logic
505
- # ==============================================================================
506
- async def trigger_training_cycle():
507
- if sys_state.cycle_running or len(trade_manager.open_positions) > 0: return "⚠️ Busy."
508
- sys_state.training_running = True
509
- asyncio.create_task(_run_training_bg())
510
- return "⏳ Training Started..."
511
-
512
- async def _run_training_bg():
513
- try:
514
- if hub_manager:
515
- msg = await hub_manager.execute_training_session()
516
- sys_state.last_cycle_logs = f"🎓 Report: {msg}"
517
- except Exception: pass
518
- finally: sys_state.training_running = False
519
-
520
- async def manual_close_current_trade():
521
- if not trade_manager.open_positions: return "⚠️ No trade."
522
- symbol = list(trade_manager.open_positions.keys())[0]
523
- await trade_manager.force_exit_by_manager(symbol, reason="MANUAL_UI")
524
- return f"✅ Closed {symbol}."
525
-
526
- async def reset_stats_handler():
527
- if trade_manager.open_positions: return "⚠️ Close trade first."
528
- await r2.reset_all_stats_async()
529
- return "✅ Reset Done."
530
-
531
- async def toggle_auto_pilot(enable):
532
- sys_state.auto_pilot = enable
533
- return f"Auto-Pilot: {enable}"
534
-
535
- async def run_cycle_from_gradio():
536
- if sys_state.cycle_running: return "Busy."
537
- asyncio.create_task(run_unified_cycle())
538
- return "🚀 Launched."
539
-
540
- # ==============================================================================
541
- # 📊 UI Updates & Charting
542
- # ==============================================================================
543
- async def check_live_pnl_and_status(selected_view="Guardian Hydra (Total)"):
544
- """
545
- Periodic update function for Gradio UI.
546
- Updated to pull from LOG_BUFFER directly.
547
- """
548
- empty_chart = go.Figure()
549
- empty_chart.update_layout(template="plotly_dark", paper_bgcolor="#0b0f19", plot_bgcolor="#0b0f19", xaxis={'visible':False}, yaxis={'visible':False})
550
- wl_df_empty = pd.DataFrame(columns=["Coin", "Score"])
551
-
552
- # 1. Get Logs
553
- live_logs_text = "\n".join(list(LOG_BUFFER))
554
-
555
- if not sys_state.ready:
556
- return live_logs_text, "Initializing...", empty_chart, "0.0", "0.0", "0.0", "0.0", "0.0%", wl_df_empty, "Loading...", "Loading...", "Checking..."
557
-
558
- try:
559
- # 1. Portfolio Data
560
- portfolio = await r2.get_portfolio_state_async()
561
- curr_cap = portfolio.get('current_capital_usd', 100.0)
562
- first_ts = portfolio.get('first_trade_timestamp')
563
- uptime_str = calculate_duration_str(first_ts)
564
-
565
- total_t = portfolio.get('total_trades', 0)
566
- wins = portfolio.get('winning_trades', 0)
567
- losses = portfolio.get('losing_trades', 0)
568
- if losses == 0 and total_t > 0: losses = total_t - wins
569
- tot_prof = portfolio.get('total_profit_usd', 0.0)
570
- tot_loss = portfolio.get('total_loss_usd', 0.0)
571
- net_prof = tot_prof - tot_loss
572
- win_rate = (wins / total_t * 100) if total_t > 0 else 0.0
573
-
574
- # 2. Active Trade Data
575
- symbol = None; entry_p = 0.0; tp_p = 0.0; sl_p = 0.0; curr_p = 0.0; pnl_pct = 0.0; pnl_val = 0.0
576
- trade_dur_str = "--:--:--"
577
- oracle_str = "N/A"
578
-
579
- if trade_manager.open_positions:
580
- symbol = list(trade_manager.open_positions.keys())[0]
581
- trade = trade_manager.open_positions[symbol]
582
- entry_p = float(trade.get('entry_price', 0.0))
583
- tp_p = float(trade.get('tp_price', 0.0))
584
- sl_p = float(trade.get('sl_price', 0.0))
585
-
586
- trade_dur_str = calculate_duration_str(trade.get('entry_time'))
587
-
588
- strength = trade.get('initial_oracle_strength', 0.0)
589
- target_cls = trade.get('initial_oracle_class', 'N/A')
590
- oracle_str = f"Str: {strength:.2f} | {target_cls}"
591
-
592
- curr_p = await data_manager.get_latest_price_async(symbol)
593
- if curr_p > 0 and entry_p > 0:
594
- pnl_pct = ((curr_p - entry_p) / entry_p) * 100
595
- pnl_val = curr_cap * (pnl_pct / 100)
596
-
597
- final_bal = curr_cap + pnl_val
598
- color = "#00ff00" if pnl_val >= 0 else "#ff0000"
599
-
600
- # 3. HTML Construction
601
- active_trade_row = ""
602
- if symbol:
603
- active_trade_row = f"""
604
- <div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc; margin-top:5px;'>
605
- <span>⏱️ Trade:</span> <span style='color: #ffff00;'>{trade_dur_str}</span>
606
- </div>"""
607
-
608
- wallet_md = f"""
609
- <div style='background-color: #1a1a1a; padding: 15px; border-radius: 8px; border: 1px solid #333; text-align:center;'>
610
- <h3 style='margin:0; color:#888; font-size:14px;'>💰 Live Wallet</h3>
611
- <div style='font-size: 26px; font-weight: bold; color: white; margin: 5px 0;'>${final_bal:,.2f}</div>
612
- <div style='font-size: 14px; color: {color};'>({pnl_val:+,.2f} USD)</div>
613
- <hr style='border-color:#444; margin: 10px 0;'>
614
-
615
- <div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc;'>
616
- <span>⏳ System:</span> <span style='color: white;'>{uptime_str}</span>
617
- </div>
618
- {active_trade_row}
619
- <div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc; margin-top:5px;'>
620
- <span>🔮 Oracle:</span> <span style='color: #00e5ff;'>{oracle_str}</span>
621
- </div>
622
- </div>
623
- """
624
-
625
- key_map = {
626
- "Guardian Hydra (Total)": "hybrid",
627
- "Head: Crash (Panic)": "crash",
628
- "Head: Giveback (Profit)": "giveback",
629
- "Head: Stagnation (Time)": "stagnation"
630
- }
631
- target_key = key_map.get(selected_view, "hybrid")
632
- stats_data = trade_manager.ai_stats.get(target_key, {"total":0, "good":0, "saved":0.0, "missed":0.0})
633
-
634
- tot_ds = stats_data['total']
635
- ds_acc = (stats_data['good'] / tot_ds * 100) if tot_ds > 0 else 0.0
636
-
637
- history_md = f"""
638
- <div style='background-color: #1a1a1a; padding: 10px; border-radius: 8px; border: 1px solid #333; font-size: 12px;'>
639
- <h3 style='margin:0 0 5px 0; color:#888; font-size:14px;'>📊 Performance</h3>
640
- <table style='width:100%; color:white;'>
641
- <tr><td>Trades:</td><td style='text-align:right;'>{total_t}</td></tr>
642
- <tr><td>Win Rate:</td><td style='text-align:right; color:{"#00ff00" if win_rate>=50 else "#ff0000"};'>{win_rate:.1f}%</td></tr>
643
- <tr><td>Wins:</td><td style='text-align:right; color:#00ff00;'>{wins} (+${tot_prof:,.2f})</td></tr>
644
- <tr><td>Losses:</td><td style='text-align:right; color:#ff0000;'>{losses} (-${tot_loss:,.2f})</td></tr>
645
- <tr><td style='border-top:1px solid #444;'>Net:</td><td style='border-top:1px solid #444; text-align:right; color:{"#00ff00" if net_prof>=0 else "#ff0000"};'>${net_prof:,.2f}</td></tr>
646
- </table>
647
- <hr style='border-color:#444; margin: 8px 0;'>
648
- <h3 style='margin:0 0 5px 0; color: #00e5ff; font-size:14px;'>🐲 Hydra IQ ({target_key})</h3>
649
- <table style='width:100%; color:white;'>
650
- <tr><td>Interventions:</td><td style='text-align:right;'>{tot_ds}</td></tr>
651
- <tr><td>Accuracy:</td><td style='text-align:right; color:#00e5ff;'>{ds_acc:.1f}%</td></tr>
652
- <tr><td>Saved:</td><td style='text-align:right; color:#00ff00;'>${stats_data['saved']:.2f}</td></tr>
653
- <tr><td>Missed:</td><td style='text-align:right; color:#ff0000;'>${stats_data['missed']:.2f}</td></tr>
654
- </table>
655
- </div>
656
- """
657
-
658
- wl_data = [[k, f"{v.get('final_total_score',0):.2f}"] for k, v in trade_manager.watchlist.items()]
659
- wl_df = pd.DataFrame(wl_data, columns=["Coin", "Score"])
660
-
661
- status_line = f"Cycle: {'RUNNING' if sys_state.cycle_running else 'IDLE'} | Auto-Pilot: {'ON' if sys_state.auto_pilot else 'OFF'}"
662
-
663
- # 5. Full Chart
664
- fig = empty_chart
665
- if symbol and curr_p > 0:
666
- ohlcv = await data_manager.get_latest_ohlcv(symbol, '5m', 120)
667
- if ohlcv:
668
- df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
669
- df['datetime'] = pd.to_datetime(df['timestamp'], unit='ms')
670
-
671
- fig = go.Figure(data=[go.Candlestick(
672
- x=df['datetime'], open=df['open'], high=df['high'], low=df['low'], close=df['close'],
673
- increasing_line_color='#00ff00', decreasing_line_color='#ff0000', name=symbol
674
- )])
675
-
676
- if entry_p > 0:
677
- fig.add_hline(y=entry_p, line_dash="dash", line_color="white", annotation_text="ENTRY", annotation_position="top left")
678
- if tp_p > 0:
679
- fig.add_hline(y=tp_p, line_color="#00ff00", line_width=2, annotation_text="TARGET (TP)", annotation_position="top left")
680
- if sl_p > 0:
681
- fig.add_hline(y=sl_p, line_color="#ff0000", line_width=2, annotation_text="STOP LOSS", annotation_position="bottom left")
682
-
683
- fig.update_layout(
684
- template="plotly_dark",
685
- paper_bgcolor="#0b0f19",
686
- plot_bgcolor="#0b0f19",
687
- margin=dict(l=0, r=40, t=30, b=0),
688
- height=400,
689
- xaxis_rangeslider_visible=False,
690
- title=dict(text=f"{symbol} (Spot Long) | PnL: {pnl_pct:+.2f}%", font=dict(color="white"))
691
- )
692
-
693
- train_status = sys_state.training_status_msg
694
- if sys_state.training_running: train_status = "🏋️‍♂️ Training..."
695
-
696
- return (live_logs_text, status_line, fig, f"{curr_p:.6f}", f"{entry_p:.6f}", f"{tp_p:.6f}", f"{sl_p:.6f}",
697
- f"{pnl_pct:+.2f}%", wl_df, wallet_md, history_md, train_status)
698
-
699
- except Exception:
700
- return live_logs_text, "Error", empty_chart, "0", "0", "0", "0", "0%", wl_df_empty, "Err", "Err", "Err"
701
-
702
- # ==============================================================================
703
- # 🎨 Gradio UI (Crash-Proof & Full Feature)
704
- # ==============================================================================
705
- def create_gradio_ui():
706
- """
707
- Creates the Gradio Interface.
708
- """
709
- custom_css = ".gradio-container {background:#0b0f19} .dataframe {background:#1a1a1a!important} .html-box {min-height:180px} .log-box textarea {font-family: 'Consolas', monospace; font-size: 12px; background-color: #111; color: #0f0;}"
710
-
711
- with gr.Blocks(title="Titan V28.4 (Live Terminal)") as demo:
712
- gr.HTML(f"<style>{custom_css}</style>")
713
-
714
- gr.Markdown("# 🚀 Titan V28.4 (Guardian Hydra)")
715
-
716
- with gr.Row():
717
- with gr.Column(scale=3):
718
- live_chart = gr.Plot(label="Chart")
719
- with gr.Row():
720
- t_price = gr.Textbox(label="Price", interactive=False)
721
- t_pnl = gr.Textbox(label="PnL %", interactive=False)
722
- with gr.Row():
723
- t_entry = gr.Textbox(label="Entry", interactive=False)
724
- t_tp = gr.Textbox(label="TP", interactive=False)
725
- t_sl = gr.Textbox(label="SL", interactive=False)
726
-
727
- with gr.Column(scale=1):
728
- wallet_out = gr.HTML(label="Wallet", elem_classes="html-box")
729
- stats_dd = gr.Dropdown([
730
- "Guardian Hydra (Total)",
731
- "Head: Crash (Panic)",
732
- "Head: Giveback (Profit)",
733
- "Head: Stagnation (Time)"
734
- ], value="Guardian Hydra (Total)", label="View AI Stats")
735
- history_out = gr.HTML(label="Stats", elem_classes="html-box")
736
- watchlist_out = gr.DataFrame(label="Watchlist")
737
-
738
- gr.HTML("<hr style='border-color:#333'>")
739
-
740
- with gr.Row():
741
- with gr.Column(scale=1):
742
- auto_pilot = gr.Checkbox(label="✈️ Auto-Pilot", value=True)
743
- with gr.Row():
744
- btn_run = gr.Button("🚀 Scan", variant="primary")
745
- btn_close = gr.Button("🚨 Close", variant="stop")
746
- with gr.Row():
747
- btn_train = gr.Button("🎓 Train", variant="secondary")
748
- btn_reset = gr.Button("🗑️ Reset")
749
- status = gr.Markdown("Init...")
750
- train_status = gr.Textbox(label="Learning", interactive=False, value="Checking...")
751
- alert = gr.Textbox(label="Alerts", interactive=False)
752
-
753
- with gr.Column(scale=3):
754
- # Live Terminal Box
755
- logs = gr.Textbox(label="Live Terminal", lines=14, autoscroll=True, elem_classes="log-box", type="text")
756
-
757
- # Event Listeners
758
- btn_run.click(fn=run_cycle_from_gradio, outputs=alert)
759
- btn_close.click(fn=manual_close_current_trade, outputs=alert)
760
- btn_reset.click(fn=reset_stats_handler, outputs=alert)
761
- btn_train.click(fn=trigger_training_cycle, outputs=alert)
762
- auto_pilot.change(fn=toggle_auto_pilot, inputs=auto_pilot, outputs=alert)
763
-
764
- # Periodic Update (1s)
765
- gr.Timer(1).tick(fn=check_live_pnl_and_status, inputs=stats_dd,
766
- outputs=[logs, status, live_chart, t_price, t_entry, t_tp, t_sl, t_pnl, watchlist_out, wallet_out, history_out, train_status])
767
- return demo
768
-
769
- # ==============================================================================
770
- # 🏁 Server Launch
771
- # ==============================================================================
772
- fast_api_server = FastAPI(lifespan=lifespan)
773
- gradio_dashboard = create_gradio_ui()
774
- app = gr.mount_gradio_app(app=fast_api_server, blocks=gradio_dashboard, path="/")
775
-
776
- if __name__ == "__main__":
777
- import uvicorn
778
- uvicorn.run(app, host="0.0.0.0", port=7860)