Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,10 +1,8 @@
|
|
| 1 |
# ==============================================================================
|
| 2 |
-
# ๐ app.py (V70.
|
| 3 |
# ==============================================================================
|
| 4 |
-
# -
|
| 5 |
-
#
|
| 6 |
-
# 2. Transparency: Displays individual model scores (Pattern, Oracle, MC).
|
| 7 |
-
# 3. Integration: Fully compatible with Clean R2 (V41) & SmartPortfolio (V38).
|
| 8 |
# ==============================================================================
|
| 9 |
|
| 10 |
import os
|
|
@@ -109,7 +107,7 @@ class SystemState:
|
|
| 109 |
|
| 110 |
def set_ready(self):
|
| 111 |
self.ready = True
|
| 112 |
-
self.last_cycle_logs = "โ
System Ready.
|
| 113 |
logger.info("โ
System State set to READY.")
|
| 114 |
|
| 115 |
def set_cycle_start(self):
|
|
@@ -175,13 +173,10 @@ async def auto_pilot_loop():
|
|
| 175 |
if adaptive_hub and int(time.time()) % 60 == 0:
|
| 176 |
sys_state.training_status_msg = adaptive_hub.get_status()
|
| 177 |
|
| 178 |
-
# Trade Manager Watchdog
|
| 179 |
if trade_manager and len(trade_manager.open_positions) > 0:
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
# Only update logs if cycle isn't writing
|
| 183 |
-
if not sys_state.cycle_running:
|
| 184 |
-
sys_state.last_cycle_logs = trade_manager.latest_guardian_log
|
| 185 |
continue
|
| 186 |
|
| 187 |
# Trigger Auto Scan
|
|
@@ -207,7 +202,7 @@ async def auto_pilot_loop():
|
|
| 207 |
async def lifespan(app: FastAPI):
|
| 208 |
global r2, data_manager, ml_processor, adaptive_hub, trade_manager, whale_monitor, news_fetcher, senti_analyzer, sys_state
|
| 209 |
|
| 210 |
-
logger.info("\n๐ [System] Startup Sequence (Titan V70.
|
| 211 |
try:
|
| 212 |
# 1. Initialize R2 (Persistence)
|
| 213 |
r2 = R2Service()
|
|
@@ -245,6 +240,7 @@ async def lifespan(app: FastAPI):
|
|
| 245 |
logger.info("๐ง [Tuner] Continuous Tuner Injected.")
|
| 246 |
|
| 247 |
# 9. Start Sentries
|
|
|
|
| 248 |
await trade_manager.initialize_sentry_exchanges()
|
| 249 |
await trade_manager.start_sentry_loops()
|
| 250 |
|
|
@@ -311,16 +307,11 @@ async def _fetch_l2_data_task(candidate_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
| 311 |
return None
|
| 312 |
|
| 313 |
# ------------------------------------------------------------------------------
|
| 314 |
-
# 8. Unified Logic Cycle (The 5-Layer Pipeline
|
| 315 |
# ------------------------------------------------------------------------------
|
| 316 |
async def run_unified_cycle():
|
| 317 |
"""
|
| 318 |
๐ GEM-Architect: The 5-Layer Precision Pipeline
|
| 319 |
-
- Layer 1: Breadth Filter.
|
| 320 |
-
- Layer 2: Transparent Neural Analysis (Full Visibility).
|
| 321 |
-
- Layer 3: External Data Injection.
|
| 322 |
-
- Layer 4: Sniper Execution.
|
| 323 |
-
- Layer 5: Governance Approval.
|
| 324 |
"""
|
| 325 |
log_buffer = StringIO()
|
| 326 |
def log_and_print(message):
|
|
@@ -333,7 +324,7 @@ async def run_unified_cycle():
|
|
| 333 |
sys_state.set_cycle_start()
|
| 334 |
|
| 335 |
try:
|
| 336 |
-
# Sync R2 State
|
| 337 |
await trade_manager.sync_internal_state_with_r2()
|
| 338 |
|
| 339 |
# Log Active Trades
|
|
@@ -371,10 +362,7 @@ async def run_unified_cycle():
|
|
| 371 |
l2_tasks = [process_l2(c) for c in l1_candidates]
|
| 372 |
l2_results = await asyncio.gather(*l2_tasks)
|
| 373 |
|
| 374 |
-
# Filter None values
|
| 375 |
processed_l2 = [res for res in l2_results if res is not None]
|
| 376 |
-
|
| 377 |
-
# Sort ALL results by score (Pass AND Fail) to show user the best available
|
| 378 |
processed_l2.sort(key=lambda x: x['l2_score'], reverse=True)
|
| 379 |
|
| 380 |
# ๐๏ธ X-RAY MODE: Show Top 10 regardless of status
|
|
@@ -390,7 +378,6 @@ async def run_unified_cycle():
|
|
| 390 |
log_and_print(f"{status:<3} | {c['symbol']:<9} | {c['l2_score']:>5.1f} | {c['pattern_score']*100:>5.1f} | {c['oracle_score']*100:>5.1f} | {c['mc_score']*100:>4.1f}")
|
| 391 |
log_and_print("-" * 65)
|
| 392 |
|
| 393 |
-
# ๐ The Real Filter: Only Valid Ones proceed
|
| 394 |
valid_l2 = [c for c in processed_l2 if c.get('is_valid', False)]
|
| 395 |
|
| 396 |
if not valid_l2:
|
|
@@ -435,10 +422,8 @@ async def run_unified_cycle():
|
|
| 435 |
cand['final_total_score'] = l2_score + whale_bonus + news_bonus
|
| 436 |
final_l3_list.append(cand)
|
| 437 |
|
| 438 |
-
# Re-sort
|
| 439 |
final_l3_list.sort(key=lambda x: x['final_total_score'], reverse=True)
|
| 440 |
|
| 441 |
-
# Show L3 Results (Only for Survivors)
|
| 442 |
log_and_print(f" -> {len(final_l3_list)} passed to L3.")
|
| 443 |
l3_header = f"{'SYMBOL':<9} | {'L2':<5} | {'WHALE':<6} | {'NEWS':<6} | {'TOTAL':<6}"
|
| 444 |
log_and_print("-" * 60)
|
|
@@ -531,7 +516,10 @@ async def trigger_strategic_backtest():
|
|
| 531 |
return "๐งช Strategic Backtest Started."
|
| 532 |
|
| 533 |
async def manual_close_current_trade():
|
| 534 |
-
|
|
|
|
|
|
|
|
|
|
| 535 |
symbol = list(trade_manager.open_positions.keys())[0]
|
| 536 |
await trade_manager.force_exit_by_manager(symbol, reason="MANUAL_UI")
|
| 537 |
return f"โ
Closed {symbol}."
|
|
@@ -587,6 +575,7 @@ async def run_cycle_from_gradio():
|
|
| 587 |
async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
| 588 |
"""
|
| 589 |
Poller function to update Gradio UI every few seconds.
|
|
|
|
| 590 |
"""
|
| 591 |
empty_chart = go.Figure()
|
| 592 |
empty_chart.update_layout(template="plotly_dark", paper_bgcolor="#0b0f19", plot_bgcolor="#0b0f19", xaxis={'visible':False}, yaxis={'visible':False})
|
|
@@ -598,21 +587,31 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 598 |
return "Initializing...", "...", empty_chart, "0.0", "0.0", "0.0", "0.0", "0.0%", wl_df_empty, diag_df_empty, type_df_empty, "Loading...", "Loading...", "Loading..."
|
| 599 |
|
| 600 |
try:
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 604 |
free_cap = max(0.0, equity - allocated)
|
| 605 |
-
daily_pnl = sp.state.get('daily_net_pnl', 0.0)
|
| 606 |
-
is_halted = sp.state.get('is_trading_halted', False)
|
| 607 |
|
| 608 |
symbol = None; entry_p = 0.0; tp_p = 0.0; sl_p = 0.0; curr_p = 0.0; pnl_pct = 0.0; pnl_val_unrealized = 0.0
|
| 609 |
active_trade_info = ""
|
| 610 |
trade_dur_str = "--:--:--"
|
| 611 |
|
| 612 |
# --- Active Trade Details ---
|
| 613 |
-
if
|
| 614 |
-
symbol = list(
|
| 615 |
-
trade =
|
| 616 |
entry_p = float(trade.get('entry_price', 0.0))
|
| 617 |
tp_p = float(trade.get('tp_price', 0.0))
|
| 618 |
sl_p = float(trade.get('sl_price', 0.0))
|
|
@@ -623,19 +622,17 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 623 |
gov_score = decision_data.get('governance_score', 0.0)
|
| 624 |
oracle_score = decision_data.get('oracle_score', 0.0)
|
| 625 |
|
| 626 |
-
grade_color = "#ccc"
|
| 627 |
-
if gov_grade == "ULTRA": grade_color = "#ff00ff"
|
| 628 |
-
elif gov_grade == "STRONG": grade_color = "#00ff00"
|
| 629 |
-
elif gov_grade == "NORMAL": grade_color = "#00e5ff"
|
| 630 |
-
elif gov_grade == "WEAK": grade_color = "#ffff00"
|
| 631 |
-
elif gov_grade == "REJECT": grade_color = "#ff0000"
|
| 632 |
-
|
| 633 |
curr_p = await data_manager.get_latest_price_async(symbol)
|
| 634 |
if curr_p > 0 and entry_p > 0:
|
| 635 |
pnl_pct = ((curr_p - entry_p) / entry_p) * 100
|
| 636 |
size = float(trade.get('entry_capital', 0.0))
|
| 637 |
pnl_val_unrealized = size * (pnl_pct / 100)
|
| 638 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 639 |
active_trade_info = f"""
|
| 640 |
<div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc; margin-top:5px; border-top: 1px solid #333; padding-top: 5px;'>
|
| 641 |
<span>โฑ๏ธ Time:</span> <span style='color: #ffff00;'>{trade_dur_str}</span>
|
|
@@ -644,13 +641,13 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 644 |
<span>๐๏ธ Grade:</span> <span style='color: {grade_color}; font-weight:bold;'>{gov_grade} ({gov_score:.1f})</span>
|
| 645 |
</div>
|
| 646 |
<div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc; margin-top:5px;'>
|
| 647 |
-
<span>๐ฎ Oracle
|
| 648 |
</div>
|
| 649 |
"""
|
| 650 |
|
| 651 |
virtual_equity = equity + pnl_val_unrealized
|
| 652 |
active_pnl_color = "#00ff00" if pnl_val_unrealized >= 0 else "#ff0000"
|
| 653 |
-
|
| 654 |
total_t = portfolio.get('total_trades', 0)
|
| 655 |
wins = portfolio.get('winning_trades', 0)
|
| 656 |
losses = portfolio.get('losing_trades', 0)
|
|
@@ -677,9 +674,6 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 677 |
<hr style='border-color:#444; margin: 10px 0;'>
|
| 678 |
|
| 679 |
<div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc;'>
|
| 680 |
-
<span>๐ฆ
Mood:</span> <span style='color: white;'>{sp.market_trend}</span>
|
| 681 |
-
</div>
|
| 682 |
-
<div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc; margin-top:5px;'>
|
| 683 |
<span>๐ก๏ธ Status:</span> {halt_status}
|
| 684 |
</div>
|
| 685 |
{active_trade_info}
|
|
@@ -694,7 +688,9 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 694 |
"Hydra: Stagnation (Time)": "stagnation"
|
| 695 |
}
|
| 696 |
target_key = key_map.get(selected_view, "hybrid")
|
| 697 |
-
|
|
|
|
|
|
|
| 698 |
|
| 699 |
tot_ds = stats_data['total']
|
| 700 |
ds_acc = (stats_data['good'] / tot_ds * 100) if tot_ds > 0 else 0.0
|
|
@@ -823,9 +819,9 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 823 |
def create_gradio_ui():
|
| 824 |
custom_css = ".gradio-container {background:#0b0f19} .dataframe {background:#1a1a1a!important} .html-box {min-height:180px}"
|
| 825 |
|
| 826 |
-
with gr.Blocks(title="Titan V70.
|
| 827 |
|
| 828 |
-
gr.Markdown("# ๐ Titan V70.
|
| 829 |
|
| 830 |
with gr.Row():
|
| 831 |
# LEFT: Chart & Controls
|
|
|
|
| 1 |
# ==============================================================================
|
| 2 |
+
# ๐ app.py (V70.6 - GEM-Architect: R2 Source of Truth)
|
| 3 |
# ==============================================================================
|
| 4 |
+
# - Fix: All UI elements now fetch directly from R2 to prevent Split-Brain.
|
| 5 |
+
# - Features: X-Ray Mode, Neural Visibility, Atomic UI Updates.
|
|
|
|
|
|
|
| 6 |
# ==============================================================================
|
| 7 |
|
| 8 |
import os
|
|
|
|
| 107 |
|
| 108 |
def set_ready(self):
|
| 109 |
self.ready = True
|
| 110 |
+
self.last_cycle_logs = "โ
System Ready. R2 Source of Truth Mode."
|
| 111 |
logger.info("โ
System State set to READY.")
|
| 112 |
|
| 113 |
def set_cycle_start(self):
|
|
|
|
| 173 |
if adaptive_hub and int(time.time()) % 60 == 0:
|
| 174 |
sys_state.training_status_msg = adaptive_hub.get_status()
|
| 175 |
|
| 176 |
+
# Trade Manager Watchdog (Reduced freq)
|
| 177 |
if trade_manager and len(trade_manager.open_positions) > 0:
|
| 178 |
+
if not sys_state.cycle_running:
|
| 179 |
+
sys_state.last_cycle_logs = trade_manager.latest_guardian_log
|
|
|
|
|
|
|
|
|
|
| 180 |
continue
|
| 181 |
|
| 182 |
# Trigger Auto Scan
|
|
|
|
| 202 |
async def lifespan(app: FastAPI):
|
| 203 |
global r2, data_manager, ml_processor, adaptive_hub, trade_manager, whale_monitor, news_fetcher, senti_analyzer, sys_state
|
| 204 |
|
| 205 |
+
logger.info("\n๐ [System] Startup Sequence (Titan V70.6 - R2 Sync)...")
|
| 206 |
try:
|
| 207 |
# 1. Initialize R2 (Persistence)
|
| 208 |
r2 = R2Service()
|
|
|
|
| 240 |
logger.info("๐ง [Tuner] Continuous Tuner Injected.")
|
| 241 |
|
| 242 |
# 9. Start Sentries
|
| 243 |
+
await trade_manager.sync_internal_state_with_r2()
|
| 244 |
await trade_manager.initialize_sentry_exchanges()
|
| 245 |
await trade_manager.start_sentry_loops()
|
| 246 |
|
|
|
|
| 307 |
return None
|
| 308 |
|
| 309 |
# ------------------------------------------------------------------------------
|
| 310 |
+
# 8. Unified Logic Cycle (The 5-Layer Pipeline)
|
| 311 |
# ------------------------------------------------------------------------------
|
| 312 |
async def run_unified_cycle():
|
| 313 |
"""
|
| 314 |
๐ GEM-Architect: The 5-Layer Precision Pipeline
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
"""
|
| 316 |
log_buffer = StringIO()
|
| 317 |
def log_and_print(message):
|
|
|
|
| 324 |
sys_state.set_cycle_start()
|
| 325 |
|
| 326 |
try:
|
| 327 |
+
# Sync R2 State (Crucial)
|
| 328 |
await trade_manager.sync_internal_state_with_r2()
|
| 329 |
|
| 330 |
# Log Active Trades
|
|
|
|
| 362 |
l2_tasks = [process_l2(c) for c in l1_candidates]
|
| 363 |
l2_results = await asyncio.gather(*l2_tasks)
|
| 364 |
|
|
|
|
| 365 |
processed_l2 = [res for res in l2_results if res is not None]
|
|
|
|
|
|
|
| 366 |
processed_l2.sort(key=lambda x: x['l2_score'], reverse=True)
|
| 367 |
|
| 368 |
# ๐๏ธ X-RAY MODE: Show Top 10 regardless of status
|
|
|
|
| 378 |
log_and_print(f"{status:<3} | {c['symbol']:<9} | {c['l2_score']:>5.1f} | {c['pattern_score']*100:>5.1f} | {c['oracle_score']*100:>5.1f} | {c['mc_score']*100:>4.1f}")
|
| 379 |
log_and_print("-" * 65)
|
| 380 |
|
|
|
|
| 381 |
valid_l2 = [c for c in processed_l2 if c.get('is_valid', False)]
|
| 382 |
|
| 383 |
if not valid_l2:
|
|
|
|
| 422 |
cand['final_total_score'] = l2_score + whale_bonus + news_bonus
|
| 423 |
final_l3_list.append(cand)
|
| 424 |
|
|
|
|
| 425 |
final_l3_list.sort(key=lambda x: x['final_total_score'], reverse=True)
|
| 426 |
|
|
|
|
| 427 |
log_and_print(f" -> {len(final_l3_list)} passed to L3.")
|
| 428 |
l3_header = f"{'SYMBOL':<9} | {'L2':<5} | {'WHALE':<6} | {'NEWS':<6} | {'TOTAL':<6}"
|
| 429 |
log_and_print("-" * 60)
|
|
|
|
| 516 |
return "๐งช Strategic Backtest Started."
|
| 517 |
|
| 518 |
async def manual_close_current_trade():
|
| 519 |
+
# Sync first to be sure
|
| 520 |
+
await trade_manager.sync_internal_state_with_r2()
|
| 521 |
+
if not trade_manager.open_positions: return "โ ๏ธ No trade found in R2."
|
| 522 |
+
|
| 523 |
symbol = list(trade_manager.open_positions.keys())[0]
|
| 524 |
await trade_manager.force_exit_by_manager(symbol, reason="MANUAL_UI")
|
| 525 |
return f"โ
Closed {symbol}."
|
|
|
|
| 575 |
async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
| 576 |
"""
|
| 577 |
Poller function to update Gradio UI every few seconds.
|
| 578 |
+
NOW PURELY R2-BASED TO PREVENT SPLIT-BRAIN.
|
| 579 |
"""
|
| 580 |
empty_chart = go.Figure()
|
| 581 |
empty_chart.update_layout(template="plotly_dark", paper_bgcolor="#0b0f19", plot_bgcolor="#0b0f19", xaxis={'visible':False}, yaxis={'visible':False})
|
|
|
|
| 587 |
return "Initializing...", "...", empty_chart, "0.0", "0.0", "0.0", "0.0", "0.0%", wl_df_empty, diag_df_empty, type_df_empty, "Loading...", "Loading...", "Loading..."
|
| 588 |
|
| 589 |
try:
|
| 590 |
+
# ๐ฅ DIRECT R2 FETCH (Source of Truth)
|
| 591 |
+
portfolio = await r2.get_portfolio_state_async()
|
| 592 |
+
open_trades_raw = await r2.get_open_trades_async()
|
| 593 |
+
|
| 594 |
+
# Parse Trades
|
| 595 |
+
active_trades_dict = {}
|
| 596 |
+
if isinstance(open_trades_raw, list):
|
| 597 |
+
for t in open_trades_raw: active_trades_dict[t['symbol']] = t
|
| 598 |
+
elif isinstance(open_trades_raw, dict):
|
| 599 |
+
active_trades_dict = open_trades_raw
|
| 600 |
+
|
| 601 |
+
equity = portfolio.get('current_capital_usd', INITIAL_CAPITAL)
|
| 602 |
+
daily_pnl = portfolio.get('daily_net_pnl', 0.0)
|
| 603 |
+
allocated = portfolio.get('allocated_capital_usd', 0.0)
|
| 604 |
+
is_halted = portfolio.get('is_trading_halted', False)
|
| 605 |
free_cap = max(0.0, equity - allocated)
|
|
|
|
|
|
|
| 606 |
|
| 607 |
symbol = None; entry_p = 0.0; tp_p = 0.0; sl_p = 0.0; curr_p = 0.0; pnl_pct = 0.0; pnl_val_unrealized = 0.0
|
| 608 |
active_trade_info = ""
|
| 609 |
trade_dur_str = "--:--:--"
|
| 610 |
|
| 611 |
# --- Active Trade Details ---
|
| 612 |
+
if active_trades_dict:
|
| 613 |
+
symbol = list(active_trades_dict.keys())[0]
|
| 614 |
+
trade = active_trades_dict[symbol]
|
| 615 |
entry_p = float(trade.get('entry_price', 0.0))
|
| 616 |
tp_p = float(trade.get('tp_price', 0.0))
|
| 617 |
sl_p = float(trade.get('sl_price', 0.0))
|
|
|
|
| 622 |
gov_score = decision_data.get('governance_score', 0.0)
|
| 623 |
oracle_score = decision_data.get('oracle_score', 0.0)
|
| 624 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 625 |
curr_p = await data_manager.get_latest_price_async(symbol)
|
| 626 |
if curr_p > 0 and entry_p > 0:
|
| 627 |
pnl_pct = ((curr_p - entry_p) / entry_p) * 100
|
| 628 |
size = float(trade.get('entry_capital', 0.0))
|
| 629 |
pnl_val_unrealized = size * (pnl_pct / 100)
|
| 630 |
|
| 631 |
+
grade_color = "#ccc"
|
| 632 |
+
if gov_grade == "ULTRA": grade_color = "#ff00ff"
|
| 633 |
+
elif gov_grade == "STRONG": grade_color = "#00ff00"
|
| 634 |
+
elif gov_grade == "NORMAL": grade_color = "#00e5ff"
|
| 635 |
+
|
| 636 |
active_trade_info = f"""
|
| 637 |
<div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc; margin-top:5px; border-top: 1px solid #333; padding-top: 5px;'>
|
| 638 |
<span>โฑ๏ธ Time:</span> <span style='color: #ffff00;'>{trade_dur_str}</span>
|
|
|
|
| 641 |
<span>๐๏ธ Grade:</span> <span style='color: {grade_color}; font-weight:bold;'>{gov_grade} ({gov_score:.1f})</span>
|
| 642 |
</div>
|
| 643 |
<div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc; margin-top:5px;'>
|
| 644 |
+
<span>๐ฎ Oracle:</span> <span style='color: #00ff00;'>{oracle_score*100:+.1f}%</span>
|
| 645 |
</div>
|
| 646 |
"""
|
| 647 |
|
| 648 |
virtual_equity = equity + pnl_val_unrealized
|
| 649 |
active_pnl_color = "#00ff00" if pnl_val_unrealized >= 0 else "#ff0000"
|
| 650 |
+
|
| 651 |
total_t = portfolio.get('total_trades', 0)
|
| 652 |
wins = portfolio.get('winning_trades', 0)
|
| 653 |
losses = portfolio.get('losing_trades', 0)
|
|
|
|
| 674 |
<hr style='border-color:#444; margin: 10px 0;'>
|
| 675 |
|
| 676 |
<div style='display: flex; justify-content: space-between; font-size: 12px; color: #ccc;'>
|
|
|
|
|
|
|
|
|
|
| 677 |
<span>๐ก๏ธ Status:</span> {halt_status}
|
| 678 |
</div>
|
| 679 |
{active_trade_info}
|
|
|
|
| 688 |
"Hydra: Stagnation (Time)": "stagnation"
|
| 689 |
}
|
| 690 |
target_key = key_map.get(selected_view, "hybrid")
|
| 691 |
+
# Fetch stats from R2 (not memory)
|
| 692 |
+
stats_file = await r2.get_guardian_stats_async()
|
| 693 |
+
stats_data = stats_file.get(target_key, {"total":0, "good":0, "saved":0.0, "missed":0.0})
|
| 694 |
|
| 695 |
tot_ds = stats_data['total']
|
| 696 |
ds_acc = (stats_data['good'] / tot_ds * 100) if tot_ds > 0 else 0.0
|
|
|
|
| 819 |
def create_gradio_ui():
|
| 820 |
custom_css = ".gradio-container {background:#0b0f19} .dataframe {background:#1a1a1a!important} .html-box {min-height:180px}"
|
| 821 |
|
| 822 |
+
with gr.Blocks(title="Titan V70.6 (R2-Sync)", css=custom_css) as demo:
|
| 823 |
|
| 824 |
+
gr.Markdown("# ๐ Titan V70.6 (Neural Core + R2 Sync)")
|
| 825 |
|
| 826 |
with gr.Row():
|
| 827 |
# LEFT: Chart & Controls
|