Riy777 commited on
Commit
68141b8
ยท
verified ยท
1 Parent(s): b8afcfc

Upload app (73).py

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