Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,10 +1,9 @@
|
|
| 1 |
# ==============================================================================
|
| 2 |
-
# π app.py (
|
| 3 |
# ==============================================================================
|
| 4 |
-
# - Architecture:
|
| 5 |
-
# -
|
| 6 |
-
# -
|
| 7 |
-
# - UI: Full Gradio Dashboard with Charting, Logging, and Manual Controls.
|
| 8 |
# ==============================================================================
|
| 9 |
|
| 10 |
import os
|
|
@@ -109,7 +108,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):
|
|
@@ -207,7 +206,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
|
| 211 |
try:
|
| 212 |
# 1. Initialize R2 (Persistence)
|
| 213 |
r2 = R2Service()
|
|
@@ -231,7 +230,7 @@ async def lifespan(app: FastAPI):
|
|
| 231 |
adaptive_hub = AdaptiveHub(r2_service=r2)
|
| 232 |
await adaptive_hub.initialize()
|
| 233 |
|
| 234 |
-
# 6. Initialize ML Processor (
|
| 235 |
ml_processor = MLProcessor(data_manager=data_manager)
|
| 236 |
await ml_processor.initialize()
|
| 237 |
|
|
@@ -268,16 +267,16 @@ async def lifespan(app: FastAPI):
|
|
| 268 |
logger.info("β
[System] Shutdown Complete.")
|
| 269 |
|
| 270 |
# ------------------------------------------------------------------------------
|
| 271 |
-
# 7. Analysis Tasks (
|
| 272 |
# ------------------------------------------------------------------------------
|
| 273 |
-
async def
|
| 274 |
"""
|
| 275 |
-
|
| 276 |
-
|
| 277 |
"""
|
| 278 |
try:
|
| 279 |
symbol = candidate_data['symbol']
|
| 280 |
-
required_tfs = ["
|
| 281 |
|
| 282 |
# Concurrent Data Fetching
|
| 283 |
data_tasks = [data_manager.get_latest_ohlcv(symbol, tf, limit=300) for tf in required_tfs]
|
|
@@ -288,7 +287,7 @@ async def _analyze_symbol_task(candidate_data: Dict[str, Any]) -> Dict[str, Any]
|
|
| 288 |
if data and len(data) > 0: ohlcv_data[tf] = data
|
| 289 |
|
| 290 |
# Validation: Ensure critical timeframes exist
|
| 291 |
-
if '
|
| 292 |
return None
|
| 293 |
|
| 294 |
current_price = await data_manager.get_latest_price_async(symbol)
|
|
@@ -305,24 +304,22 @@ async def _analyze_symbol_task(candidate_data: Dict[str, Any]) -> Dict[str, Any]
|
|
| 305 |
'strategy_type': candidate_data.get('strategy_type', 'NORMAL'),
|
| 306 |
'l1_score': candidate_data.get('l1_sort_score', 0)
|
| 307 |
}
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
res = await ml_processor.process_compound_signal(raw_data)
|
| 311 |
-
if not res: return None
|
| 312 |
-
|
| 313 |
-
# Pass through strategy type
|
| 314 |
-
res['strategy_type'] = candidate_data.get('strategy_type', 'NORMAL')
|
| 315 |
-
return res
|
| 316 |
except Exception:
|
| 317 |
return None
|
| 318 |
|
| 319 |
# ------------------------------------------------------------------------------
|
| 320 |
-
# 8. Unified Logic Cycle (The
|
| 321 |
# ------------------------------------------------------------------------------
|
| 322 |
async def run_unified_cycle():
|
| 323 |
"""
|
| 324 |
-
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
"""
|
| 327 |
log_buffer = StringIO()
|
| 328 |
def log_and_print(message):
|
|
@@ -347,160 +344,158 @@ async def run_unified_cycle():
|
|
| 347 |
pnl = ((curr_p - entry_p) / entry_p) * 100 if entry_p > 0 else 0
|
| 348 |
log_and_print(f" π {sym}: {pnl:+.2f}%")
|
| 349 |
|
| 350 |
-
#
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
|
|
|
|
|
|
|
|
|
| 355 |
sys_state.set_cycle_end(logs=log_buffer.getvalue())
|
| 356 |
return
|
| 357 |
|
| 358 |
-
#
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
#
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
sys_state.set_cycle_end(logs=log_buffer.getvalue())
|
| 385 |
return
|
| 386 |
|
| 387 |
-
#
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 397 |
|
| 398 |
# Whale Analysis
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
w_data = await whale_monitor.get_symbol_whale_activity(symbol, known_price=
|
| 403 |
-
if w_data and w_data.get('
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
dynamic_impact = SystemLimits.L3_WHALE_IMPACT_MAX * confidence
|
| 408 |
-
if action == 'BUY': whale_points = dynamic_impact
|
| 409 |
-
elif action == 'SELL': whale_points = -dynamic_impact
|
| 410 |
-
except Exception: pass
|
| 411 |
|
| 412 |
# News Analysis
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
n_data = await news_fetcher.get_news(symbol)
|
| 417 |
-
|
| 418 |
-
if "No specific news" not in
|
| 419 |
-
sent = senti_analyzer.polarity_scores(
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
mc_a_points = 0.0
|
| 426 |
-
try:
|
| 427 |
-
raw_mc_a = await ml_processor.run_advanced_monte_carlo(symbol, '1h')
|
| 428 |
-
mc_a_points = max(-SystemLimits.L3_MC_ADVANCED_MAX, min(SystemLimits.L3_MC_ADVANCED_MAX, raw_mc_a))
|
| 429 |
-
except Exception: pass
|
| 430 |
-
|
| 431 |
-
# Calculate Final Score (Pre-Oracle)
|
| 432 |
-
final_score = l2_score + whale_points + news_points + mc_a_points
|
| 433 |
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 439 |
|
| 440 |
-
|
|
|
|
| 441 |
|
| 442 |
-
|
|
|
|
|
|
|
|
|
|
| 443 |
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
f"{'NEWS':<5} | {'WHALE':<6} | {'FINAL':<6} | {'EXP.RET':<8} | {'STATUS'}")
|
| 447 |
-
|
| 448 |
-
log_and_print("-" * 140)
|
| 449 |
-
log_and_print(header)
|
| 450 |
-
log_and_print("-" * 140)
|
| 451 |
|
| 452 |
-
|
| 453 |
-
|
|
|
|
| 454 |
|
| 455 |
-
#
|
| 456 |
-
|
|
|
|
| 457 |
|
| 458 |
-
|
| 459 |
-
oracle_score = decision.get('oracle_score', 0.0) # Expected PnL from LightGBM
|
| 460 |
-
target_class = decision.get('target_class', '')
|
| 461 |
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
if action == 'WATCH' or action == 'BUY':
|
| 465 |
-
status_str = f"β
{target_class}"
|
| 466 |
-
sig.update(decision)
|
| 467 |
-
approved_signals.append(sig)
|
| 468 |
-
|
| 469 |
-
titan_d = sig.get('titan_score', 0.0)
|
| 470 |
-
news_d = sig.get('news_score', 0.0)
|
| 471 |
-
whale_d = sig.get('whale_score', 0.0)
|
| 472 |
-
mc_d = sig.get('mc_score', 0.0)
|
| 473 |
-
final_d = sig.get('final_total_score', 0.0)
|
| 474 |
-
strat_type = sig.get('strategy_type', 'N/A')
|
| 475 |
-
|
| 476 |
-
log_and_print(
|
| 477 |
-
f"{symbol:<9} | "
|
| 478 |
-
f"{strat_type:<8} | "
|
| 479 |
-
f"{titan_d:.1f} | "
|
| 480 |
-
f"{mc_d:.1f} | "
|
| 481 |
-
f"{news_d:+.1f} | "
|
| 482 |
-
f"{whale_d:+.1f} | "
|
| 483 |
-
f"{final_d:.1f} | "
|
| 484 |
-
f"{oracle_score*100:+.2f}% | " # Display Expected Return
|
| 485 |
-
f"{status_str}"
|
| 486 |
-
)
|
| 487 |
-
|
| 488 |
-
# --- PHASE 4: Execution & Governance ---
|
| 489 |
-
if approved_signals:
|
| 490 |
-
log_and_print("-" * 140)
|
| 491 |
-
log_and_print(f" [4/5] π― L4 Sniper -> ποΈ Governance -> π° Portfolio ({len(approved_signals)} candidates)...")
|
| 492 |
-
tm_log_buffer = StringIO()
|
| 493 |
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 503 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
gc.collect()
|
| 505 |
sys_state.set_cycle_end(logs=log_buffer.getvalue())
|
| 506 |
|
|
@@ -633,7 +628,7 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 633 |
decision_data = trade.get('decision_data', {})
|
| 634 |
gov_grade = decision_data.get('governance_grade', 'N/A')
|
| 635 |
gov_score = decision_data.get('governance_score', 0.0)
|
| 636 |
-
oracle_score = decision_data.get('oracle_score', 0.0)
|
| 637 |
|
| 638 |
grade_color = "#ccc"
|
| 639 |
if gov_grade == "ULTRA": grade_color = "#ff00ff"
|
|
@@ -756,11 +751,17 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 756 |
# --- DataFrames ---
|
| 757 |
diag_data = await r2.get_diagnostic_stats_async()
|
| 758 |
diag_list = []
|
| 759 |
-
#
|
| 760 |
-
ordered_models = ["Titan", "Oracle", "Sniper", "MonteCarlo_L", "MonteCarlo_A", "News", "Governance"]
|
| 761 |
|
| 762 |
for m in ordered_models:
|
| 763 |
stats = diag_data.get(m, {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 |
profit_accum = stats.get('profit_accum', 0.0)
|
| 765 |
loss_accum = stats.get('loss_accum', 0.0)
|
| 766 |
if profit_accum == 0.0 and loss_accum == 0.0 and stats['pnl'] != 0.0:
|
|
@@ -768,7 +769,11 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 768 |
else: loss_accum = abs(stats['pnl'])
|
| 769 |
|
| 770 |
pnl_str = format_pnl_split(profit_accum, loss_accum)
|
| 771 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 772 |
|
| 773 |
diag_df = pd.DataFrame(diag_list, columns=["Model", "Wins", "Losses", "PnL (USD)"])
|
| 774 |
|
|
@@ -835,9 +840,9 @@ async def check_live_pnl_and_status(selected_view="Dual-Core (Hybrid)"):
|
|
| 835 |
def create_gradio_ui():
|
| 836 |
custom_css = ".gradio-container {background:#0b0f19} .dataframe {background:#1a1a1a!important} .html-box {min-height:180px}"
|
| 837 |
|
| 838 |
-
with gr.Blocks(title="Titan
|
| 839 |
|
| 840 |
-
gr.Markdown("# π Titan
|
| 841 |
|
| 842 |
with gr.Row():
|
| 843 |
# LEFT: Chart & Controls
|
|
|
|
| 1 |
# ==============================================================================
|
| 2 |
+
# π app.py (V70.0 - GEM-Architect: The 5-Layer Neural Core)
|
| 3 |
# ==============================================================================
|
| 4 |
+
# - Architecture: PatternNet (ResNet) + Oracle V4.5 + Sniper V2.
|
| 5 |
+
# - Logic: 5-Stage Precision Funnel (Breadth -> Neural -> External -> Micro -> Governance).
|
| 6 |
+
# - UI: Full Gradio Dashboard with Real-time Monitoring.
|
|
|
|
| 7 |
# ==============================================================================
|
| 8 |
|
| 9 |
import os
|
|
|
|
| 108 |
|
| 109 |
def set_ready(self):
|
| 110 |
self.ready = True
|
| 111 |
+
self.last_cycle_logs = "β
System Ready. PatternNet (ResNet) Online."
|
| 112 |
logger.info("β
System State set to READY.")
|
| 113 |
|
| 114 |
def set_cycle_start(self):
|
|
|
|
| 206 |
async def lifespan(app: FastAPI):
|
| 207 |
global r2, data_manager, ml_processor, adaptive_hub, trade_manager, whale_monitor, news_fetcher, senti_analyzer, sys_state
|
| 208 |
|
| 209 |
+
logger.info("\nπ [System] Startup Sequence (Titan V70.0 - 5-Layer Core)...")
|
| 210 |
try:
|
| 211 |
# 1. Initialize R2 (Persistence)
|
| 212 |
r2 = R2Service()
|
|
|
|
| 230 |
adaptive_hub = AdaptiveHub(r2_service=r2)
|
| 231 |
await adaptive_hub.initialize()
|
| 232 |
|
| 233 |
+
# 6. Initialize ML Processor (PatternNet + Oracle + Sniper)
|
| 234 |
ml_processor = MLProcessor(data_manager=data_manager)
|
| 235 |
await ml_processor.initialize()
|
| 236 |
|
|
|
|
| 267 |
logger.info("β
[System] Shutdown Complete.")
|
| 268 |
|
| 269 |
# ------------------------------------------------------------------------------
|
| 270 |
+
# 7. Analysis Tasks (Data Fetcher for Layers)
|
| 271 |
# ------------------------------------------------------------------------------
|
| 272 |
+
async def _fetch_l2_data_task(candidate_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 273 |
"""
|
| 274 |
+
Fetches required 15m, 1h, 4h data for L2 (Pattern/Oracle/MC).
|
| 275 |
+
Does NOT run the models, just prepares the data package.
|
| 276 |
"""
|
| 277 |
try:
|
| 278 |
symbol = candidate_data['symbol']
|
| 279 |
+
required_tfs = ["15m", "1h", "4h"] # PatternNet needs 15m, MC needs 1h
|
| 280 |
|
| 281 |
# Concurrent Data Fetching
|
| 282 |
data_tasks = [data_manager.get_latest_ohlcv(symbol, tf, limit=300) for tf in required_tfs]
|
|
|
|
| 287 |
if data and len(data) > 0: ohlcv_data[tf] = data
|
| 288 |
|
| 289 |
# Validation: Ensure critical timeframes exist
|
| 290 |
+
if '15m' not in ohlcv_data:
|
| 291 |
return None
|
| 292 |
|
| 293 |
current_price = await data_manager.get_latest_price_async(symbol)
|
|
|
|
| 304 |
'strategy_type': candidate_data.get('strategy_type', 'NORMAL'),
|
| 305 |
'l1_score': candidate_data.get('l1_sort_score', 0)
|
| 306 |
}
|
| 307 |
+
return raw_data
|
| 308 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
except Exception:
|
| 310 |
return None
|
| 311 |
|
| 312 |
# ------------------------------------------------------------------------------
|
| 313 |
+
# 8. Unified Logic Cycle (The 5-Layer Pipeline)
|
| 314 |
# ------------------------------------------------------------------------------
|
| 315 |
async def run_unified_cycle():
|
| 316 |
"""
|
| 317 |
+
π GEM-Architect: The 5-Layer Precision Pipeline
|
| 318 |
+
Layer 1: Breadth Filter (Data Manager)
|
| 319 |
+
Layer 2: Pattern + Oracle + MC (Score & Gate) -> Limit to Top 10
|
| 320 |
+
Layer 3: External Data (News + Whales) -> Re-Rank Top 10
|
| 321 |
+
Layer 4: Sniper (Micro-Structure) -> Pick Best 1
|
| 322 |
+
Layer 5: Governance (Senate) -> Final Approval & Execution
|
| 323 |
"""
|
| 324 |
log_buffer = StringIO()
|
| 325 |
def log_and_print(message):
|
|
|
|
| 344 |
pnl = ((curr_p - entry_p) / entry_p) * 100 if entry_p > 0 else 0
|
| 345 |
log_and_print(f" π {sym}: {pnl:+.2f}%")
|
| 346 |
|
| 347 |
+
# ==============================================================================
|
| 348 |
+
# π LAYER 1: Market Breadth & Liquidity Filter
|
| 349 |
+
# ==============================================================================
|
| 350 |
+
log_and_print(f" [1/5] π Layer 1: Market Breadth & Liquidity Screen...")
|
| 351 |
+
l1_candidates = await data_manager.layer1_rapid_screening(limit=200, adaptive_hub_ref=adaptive_hub)
|
| 352 |
+
|
| 353 |
+
if not l1_candidates:
|
| 354 |
+
log_and_print("β οΈ [Layer 1] No candidates found (Market unsafe or flat).")
|
| 355 |
sys_state.set_cycle_end(logs=log_buffer.getvalue())
|
| 356 |
return
|
| 357 |
|
| 358 |
+
# ==============================================================================
|
| 359 |
+
# π§ LAYER 2: Neural Analysis (PatternNet + Oracle + MC)
|
| 360 |
+
# ==============================================================================
|
| 361 |
+
log_and_print(f" [2/5] π§ Layer 2: Neural Pattern Analysis ({len(l1_candidates)} coins)...")
|
| 362 |
+
|
| 363 |
+
# Helper wrapper for concurrency
|
| 364 |
+
async def process_l2(cand):
|
| 365 |
+
raw_data = await _fetch_l2_data_task(cand)
|
| 366 |
+
if not raw_data: return None
|
| 367 |
+
return await ml_processor.execute_layer2_analysis(raw_data)
|
| 368 |
+
|
| 369 |
+
# Batch execute L2
|
| 370 |
+
l2_tasks = [process_l2(c) for c in l1_candidates]
|
| 371 |
+
l2_results = await asyncio.gather(*l2_tasks)
|
| 372 |
+
|
| 373 |
+
# Filter failures (None)
|
| 374 |
+
valid_l2 = [res for res in l2_results if res is not None]
|
| 375 |
+
|
| 376 |
+
# Sort by Composite Score and Keep TOP 10
|
| 377 |
+
valid_l2.sort(key=lambda x: x['l2_score'], reverse=True)
|
| 378 |
+
top_10_candidates = valid_l2[:10]
|
| 379 |
+
|
| 380 |
+
if not top_10_candidates:
|
| 381 |
+
log_and_print("β οΈ [Layer 2] All candidates failed Pattern Gate (<40%).")
|
| 382 |
+
# Log top rejected for debug
|
|
|
|
| 383 |
sys_state.set_cycle_end(logs=log_buffer.getvalue())
|
| 384 |
return
|
| 385 |
|
| 386 |
+
# ==============================================================================
|
| 387 |
+
# π‘ LAYER 3: External Intelligence (News + Whales)
|
| 388 |
+
# ==============================================================================
|
| 389 |
+
log_and_print(f" [3/5] π‘ Layer 3: External Intelligence Injection (Top {len(top_10_candidates)})...")
|
| 390 |
+
final_l3_list = []
|
| 391 |
+
|
| 392 |
+
# Header for L3 Table
|
| 393 |
+
header = (f"{'SYM':<9} | {'PATT':<5} | {'MC':<4} | {'ORCL':<5} | "
|
| 394 |
+
f"{'WHALE':<5} | {'NEWS':<5} | {'TOTAL':<6}")
|
| 395 |
+
log_and_print("-" * 60)
|
| 396 |
+
log_and_print(header)
|
| 397 |
+
|
| 398 |
+
for cand in top_10_candidates:
|
| 399 |
+
symbol = cand['symbol']
|
| 400 |
+
l2_score = cand['l2_score']
|
| 401 |
|
| 402 |
# Whale Analysis
|
| 403 |
+
whale_bonus = 0.0
|
| 404 |
+
if whale_monitor:
|
| 405 |
+
try:
|
| 406 |
+
w_data = await whale_monitor.get_symbol_whale_activity(symbol, known_price=cand.get('current_price', 0))
|
| 407 |
+
if w_data and w_data.get('trading_signal', {}).get('action') == 'BUY':
|
| 408 |
+
conf = float(w_data.get('trading_signal', {}).get('confidence', 0.5))
|
| 409 |
+
whale_bonus = SystemLimits.L3_WHALE_IMPACT_MAX * conf
|
| 410 |
+
except: pass
|
|
|
|
|
|
|
|
|
|
|
|
|
| 411 |
|
| 412 |
# News Analysis
|
| 413 |
+
news_bonus = 0.0
|
| 414 |
+
if news_fetcher and senti_analyzer:
|
| 415 |
+
try:
|
| 416 |
n_data = await news_fetcher.get_news(symbol)
|
| 417 |
+
summary = n_data.get('summary', '')
|
| 418 |
+
if "No specific news" not in summary:
|
| 419 |
+
sent = senti_analyzer.polarity_scores(summary)
|
| 420 |
+
news_bonus = sent['compound'] * SystemLimits.L3_NEWS_IMPACT_MAX
|
| 421 |
+
except: pass
|
| 422 |
+
|
| 423 |
+
# Final Score Calculation
|
| 424 |
+
final_score = l2_score + whale_bonus + news_bonus
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
|
| 426 |
+
cand['whale_score'] = whale_bonus
|
| 427 |
+
cand['news_score'] = news_bonus
|
| 428 |
+
cand['final_total_score'] = final_score
|
| 429 |
+
|
| 430 |
+
log_and_print(
|
| 431 |
+
f"{symbol:<9} | "
|
| 432 |
+
f"{cand['pattern_score']*100:.1f} | "
|
| 433 |
+
f"{cand['mc_score']*100:.1f} | "
|
| 434 |
+
f"{cand['oracle_score']*100:.1f} | "
|
| 435 |
+
f"{whale_bonus:+.1f} | "
|
| 436 |
+
f"{news_bonus:+.1f} | "
|
| 437 |
+
f"{final_score:.1f}"
|
| 438 |
+
)
|
| 439 |
+
final_l3_list.append(cand)
|
| 440 |
|
| 441 |
+
# Re-sort based on L3 Total
|
| 442 |
+
final_l3_list.sort(key=lambda x: x['final_total_score'], reverse=True)
|
| 443 |
|
| 444 |
+
# ==============================================================================
|
| 445 |
+
# π― LAYER 4: Sniper (Micro-Structure) - Find The One
|
| 446 |
+
# ==============================================================================
|
| 447 |
+
log_and_print(f" [4/5] π― Layer 4: Sniper Micro-Analysis (Scanning Top 5)...")
|
| 448 |
|
| 449 |
+
best_candidate = None
|
| 450 |
+
best_sniper_score = -1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
|
| 452 |
+
# Scan top 5 only to save API/Processing
|
| 453 |
+
for cand in final_l3_list[:5]:
|
| 454 |
+
symbol = cand['symbol']
|
| 455 |
|
| 456 |
+
# Fetch 1m Data & Order Book
|
| 457 |
+
t1m = await data_manager.get_latest_ohlcv(symbol, '1m', 500)
|
| 458 |
+
ob = await data_manager.get_order_book_snapshot(symbol)
|
| 459 |
|
| 460 |
+
if not t1m or not ob: continue
|
|
|
|
|
|
|
| 461 |
|
| 462 |
+
# Run Sniper
|
| 463 |
+
sniper_res = await ml_processor.execute_layer4_sniper(symbol, t1m, ob)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
|
| 465 |
+
s_sig = sniper_res.get('signal', 'WAIT')
|
| 466 |
+
s_conf = sniper_res.get('confidence_prob', 0.0)
|
| 467 |
+
s_reason = sniper_res.get('reason', 'N/A')
|
| 468 |
|
| 469 |
+
log_and_print(f" π {symbol:<6} -> Signal: {s_sig} | Conf: {s_conf:.2f} | {s_reason}")
|
| 470 |
+
|
| 471 |
+
# Logic: Must be BUY and have highest score
|
| 472 |
+
if s_sig == 'BUY' and s_conf > best_sniper_score:
|
| 473 |
+
best_sniper_score = s_conf
|
| 474 |
+
cand['sniper_entry_price'] = sniper_res.get('entry_price', 0)
|
| 475 |
+
cand['sniper_score'] = s_conf
|
| 476 |
+
best_candidate = cand
|
| 477 |
+
|
| 478 |
+
if not best_candidate:
|
| 479 |
+
log_and_print("π Layer 4: Sniper found no valid entry points in top candidates.")
|
| 480 |
+
sys_state.set_cycle_end(logs=log_buffer.getvalue())
|
| 481 |
+
return
|
| 482 |
|
| 483 |
+
# ==============================================================================
|
| 484 |
+
# ποΈ LAYER 5: Governance & Execution
|
| 485 |
+
# ==============================================================================
|
| 486 |
+
log_and_print(f" [5/5] ποΈ Layer 5: Sending {best_candidate['symbol']} to Governance...")
|
| 487 |
+
|
| 488 |
+
tm_log_buffer = StringIO()
|
| 489 |
+
|
| 490 |
+
# Capture Trade Manager Output
|
| 491 |
+
with redirect_stdout(tm_log_buffer), redirect_stderr(tm_log_buffer):
|
| 492 |
+
# Pass as a list because TM expects a list of candidates to confirm
|
| 493 |
+
await trade_manager.select_and_execute_best_signal([best_candidate])
|
| 494 |
+
|
| 495 |
+
tm_logs = tm_log_buffer.getvalue()
|
| 496 |
+
for line in tm_logs.splitlines():
|
| 497 |
+
if line.strip(): log_and_print(line.strip())
|
| 498 |
+
|
| 499 |
gc.collect()
|
| 500 |
sys_state.set_cycle_end(logs=log_buffer.getvalue())
|
| 501 |
|
|
|
|
| 628 |
decision_data = trade.get('decision_data', {})
|
| 629 |
gov_grade = decision_data.get('governance_grade', 'N/A')
|
| 630 |
gov_score = decision_data.get('governance_score', 0.0)
|
| 631 |
+
oracle_score = decision_data.get('oracle_score', 0.0)
|
| 632 |
|
| 633 |
grade_color = "#ccc"
|
| 634 |
if gov_grade == "ULTRA": grade_color = "#ff00ff"
|
|
|
|
| 751 |
# --- DataFrames ---
|
| 752 |
diag_data = await r2.get_diagnostic_stats_async()
|
| 753 |
diag_list = []
|
| 754 |
+
# Updated Keys: Titan -> Pattern (Backwards compatibility logic)
|
| 755 |
+
ordered_models = ["Titan", "Pattern", "Oracle", "Sniper", "MonteCarlo_L", "MonteCarlo_A", "News", "Governance"]
|
| 756 |
|
| 757 |
for m in ordered_models:
|
| 758 |
stats = diag_data.get(m, {"wins": 0, "losses": 0, "pnl": 0.0, "profit_accum": 0.0, "loss_accum": 0.0})
|
| 759 |
+
|
| 760 |
+
# Merge Titan & Pattern stats if needed
|
| 761 |
+
if m == "Pattern" and "Titan" in diag_data:
|
| 762 |
+
# Optional: You can choose to display them separate or merged
|
| 763 |
+
pass
|
| 764 |
+
|
| 765 |
profit_accum = stats.get('profit_accum', 0.0)
|
| 766 |
loss_accum = stats.get('loss_accum', 0.0)
|
| 767 |
if profit_accum == 0.0 and loss_accum == 0.0 and stats['pnl'] != 0.0:
|
|
|
|
| 769 |
else: loss_accum = abs(stats['pnl'])
|
| 770 |
|
| 771 |
pnl_str = format_pnl_split(profit_accum, loss_accum)
|
| 772 |
+
# Rename Titan to Pattern in UI if found
|
| 773 |
+
display_name = "Pattern (Net)" if m == "Titan" else m
|
| 774 |
+
|
| 775 |
+
if stats['wins'] + stats['losses'] > 0:
|
| 776 |
+
diag_list.append([display_name, stats['wins'], stats['losses'], pnl_str])
|
| 777 |
|
| 778 |
diag_df = pd.DataFrame(diag_list, columns=["Model", "Wins", "Losses", "PnL (USD)"])
|
| 779 |
|
|
|
|
| 840 |
def create_gradio_ui():
|
| 841 |
custom_css = ".gradio-container {background:#0b0f19} .dataframe {background:#1a1a1a!important} .html-box {min-height:180px}"
|
| 842 |
|
| 843 |
+
with gr.Blocks(title="Titan V70.0 (5-Layer Core)", css=custom_css) as demo:
|
| 844 |
|
| 845 |
+
gr.Markdown("# π Titan V70.0 (Neural Pattern Core + 5-Layer Funnel)")
|
| 846 |
|
| 847 |
with gr.Row():
|
| 848 |
# LEFT: Chart & Controls
|