Riy777 commited on
Commit
6065b4d
·
verified ·
1 Parent(s): 4829c8c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +165 -69
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py (V17.9 - GEM-Architect: Full Stack + Intelligent Management)
2
  import os
3
  import sys
4
  import traceback
@@ -492,22 +492,97 @@ async def get_full_status():
492
  }
493
 
494
  # ==============================================================================
495
- # 📊 [ 💡 GEM-ARCHITECT FIX: FULL PRICE PRECISION ]
496
  # ==============================================================================
497
 
 
 
 
 
 
 
 
 
 
 
 
498
  async def check_live_pnl_and_status():
499
  """
500
- ديد] دالة يتم استدعاؤها كل 10 ثواني لتحديث الواجهة.
501
- (إصلاح: يستخدم 8 خانات عشرية لعرض العملات الصغيرة مثل SHIB)
502
  """
503
- global trade_manager, data_manager, sys_state
504
 
505
  empty_watchlist = pd.DataFrame(columns=["عملات المراقبة"])
 
 
 
 
506
 
507
  if not sys_state.ready:
508
- return "النظام قيد التهيئة...", "...", "...", "...", "...", None, empty_watchlist
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
509
 
510
- # 1. تحديث حالة النظام وقائمة المراقبة
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
511
  try:
512
  status_text = sys_state.last_cycle_logs
513
  trade_count = len(trade_manager.open_positions)
@@ -517,25 +592,20 @@ async def check_live_pnl_and_status():
517
  **الصفقات المفتوحة:** {trade_count} |
518
  **قائمة المراقبة:** {watch_count}
519
  """
520
-
521
  watchlist_items = list(trade_manager.watchlist.keys())
522
  watchlist_df = pd.DataFrame(watchlist_items, columns=["عملات المراقبة"])
523
-
524
- except Exception as e:
525
- return f"خطأ في جلب الحالة: {e}", "...", "...", "...", "...", None, empty_watchlist
526
 
527
- # 2. التحقق من الصفقة المفتوحة
528
  if not trade_manager.open_positions:
529
- return status_text, status_md, "### 💤 لا توجد صفقة مفتوحة", "---", "---", None, watchlist_df
530
 
531
  try:
532
  symbol = list(trade_manager.open_positions.keys())[0]
533
  trade = trade_manager.open_positions[symbol]
534
 
535
- # 3. [GEM-FIX] جلب السعر الحي ومعالجة الأصفار
536
  current_price = await data_manager.get_latest_price_async(symbol)
537
-
538
- # إذا فشل الجلب (0.0)، نستخدم سعر الدخول للعرض فقط
539
  if current_price <= 0.0:
540
  current_price = float(trade.get('entry_price', 0.0))
541
  color = "gray"
@@ -543,22 +613,12 @@ async def check_live_pnl_and_status():
543
  else:
544
  entry_price = float(trade['entry_price'])
545
  pnl_pct = ((current_price - entry_price) / entry_price) * 100
546
-
547
- # 4. تحديد اللون
548
- if pnl_pct > 0:
549
- color = "#00ff00" # Green
550
- pnl_text = f"ربح +{pnl_pct:.2f}% 🚀"
551
- elif pnl_pct < 0:
552
- color = "#ff0000" # Red
553
- pnl_text = f"خسارة {pnl_pct:.2f}% 🔻"
554
- else:
555
- color = "white"
556
- pnl_text = "تعادل 0.00%"
557
 
558
  symbol_md = f"# {symbol}"
559
- pnl_md = f"<div style='font-size: 30px; color:{color}; text-align:center; font-weight:bold;'>{pnl_text}</div>"
560
 
561
- # 5. [GEM-FIX] تنسيق الأرقام باستخدام الدالة الجديدة (Full Precision)
562
  entry_fmt = format_crypto_price(trade['entry_price'])
563
  curr_fmt = format_crypto_price(current_price)
564
  tp_fmt = format_crypto_price(trade['tp_price'])
@@ -566,54 +626,44 @@ async def check_live_pnl_and_status():
566
 
567
  details_md = f"""
568
  <div style='background-color: #222; padding: 10px; border-radius: 5px;'>
569
- <b>سعر الدخول:</b> {entry_fmt}<br>
570
- <b>السعر الحالي:</b> {curr_fmt}<br>
571
  <span style='color:#00ff00;'><b>TP:</b> {tp_fmt}</span><br>
572
  <span style='color:#ff0000;'><b>SL:</b> {sl_fmt}</span>
573
  </div>
574
  """
575
 
576
- # 6. رسم المخطط
577
  fig = None
578
  try:
579
  ohlcv = await data_manager.get_latest_ohlcv(symbol, '5m', 100)
580
  if ohlcv and len(ohlcv) > 0:
581
  df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
582
  df['datetime'] = pd.to_datetime(df['timestamp'], unit='ms')
583
-
584
  fig = plt.figure(figsize=(10, 5))
585
  ax = fig.add_subplot(111)
586
-
587
  fig.patch.set_facecolor('#0b0f19')
588
  ax.set_facecolor('#0b0f19')
589
-
590
  ax.plot(df['datetime'], df['close'], label=f'{symbol}', color='#00e5ff')
591
  ax.axhline(y=float(trade['entry_price']), color='white', linestyle='--', label='Entry')
592
  ax.axhline(y=float(trade['tp_price']), color='#00ff00', linestyle='-', label='TP')
593
  ax.axhline(y=float(trade['sl_price']), color='#ff0000', linestyle='-', label='SL')
594
-
595
  ax.tick_params(axis='x', colors='white')
596
  ax.tick_params(axis='y', colors='white')
597
  ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
598
  ax.legend(facecolor='#222', labelcolor='white')
599
-
600
  plt.tight_layout()
601
  else:
602
  fig = None
603
-
604
- except Exception as e:
605
- print(f"❌ خطأ في رسم المخطط: {e}")
606
- fig = None
607
 
608
- return status_text, status_md, symbol_md, pnl_md, details_md, fig, watchlist_df
609
 
610
  except Exception as e:
611
- print(f"❌ خطأ فادح في check_live_pnl: {e}")
612
- return status_text, status_md, f"خطأ: {e}", "...", "...", None, empty_watchlist
613
 
614
  async def run_cycle_from_gradio():
615
- if sys_state.cycle_running:
616
- return "الدورة قيد التشغيل بالفعل. يرجى الانتظار."
617
  await run_unified_cycle()
618
  return sys_state.last_cycle_logs
619
 
@@ -621,58 +671,104 @@ async def run_cycle_from_gradio():
621
  # 🚀 بناء وتثبيت واجهة Gradio
622
  # ==============================================================================
623
  def create_gradio_ui():
624
- css = "#pnl_md { text-align: center; }"
 
 
 
625
 
626
- with gr.Blocks(title="لوحة تحكم Titan", css=css, theme=gr.themes.Monochrome()) as demo:
627
- gr.Markdown("# 📊 لوحة تحكم Titan (مدمجة مع FastAPI)")
628
 
 
629
  with gr.Row():
 
630
  with gr.Column(scale=3):
631
- live_chart = gr.Plot(label="المخطط الحي للصفقة")
 
 
 
 
 
 
632
  with gr.Column(scale=1):
633
- live_symbol = gr.Markdown("### لا توجد صفقة")
634
- live_pnl = gr.Markdown("---", elem_id="pnl_md")
635
- live_details = gr.Markdown("---")
636
 
637
  gr.HTML("<hr>")
638
 
 
639
  with gr.Row():
640
  with gr.Column(scale=1):
641
- gr.Markdown("## ⚙️ لوحة التحكم")
642
- run_cycle_btn = gr.Button("🚀 (1) بدء دورة التحليل يدوياً")
643
- gr.Markdown("## 📝 حالة النظام")
644
- status_markdown = gr.Markdown(f"**الحالة:** {'جاري التهيئة...' if not sys_state.ready else 'جاهز'}")
645
- watchlist_output = gr.DataFrame(label="قائمة المراقبة (Watchlist)")
 
 
 
 
 
 
646
 
647
  with gr.Column(scale=3):
648
- gr.Markdown("## 📜 سجلات آخر دورة تحليل")
649
  cycle_logs_output = gr.Textbox(
650
- label="السجلات",
651
- lines=20,
652
  autoscroll=True,
653
  max_lines=100,
654
- value="...يتم تحميل السجلات عند انتهاء التهيئة..."
655
  )
656
 
 
 
 
657
  run_cycle_btn.click(
658
  fn=run_cycle_from_gradio,
659
  inputs=None,
660
- outputs=cycle_logs_output,
661
- api_name="run_cycle_manual"
662
  )
663
 
 
 
 
 
 
 
 
 
664
  demo.load(
665
  fn=check_live_pnl_and_status,
666
  inputs=None,
667
- outputs=[cycle_logs_output, status_markdown, live_symbol, live_pnl, live_details, live_chart, watchlist_output]
 
 
 
 
 
 
 
 
 
 
668
  )
669
 
670
- timer = gr.Timer(10)
671
-
672
  timer.tick(
673
  fn=check_live_pnl_and_status,
674
  inputs=None,
675
- outputs=[cycle_logs_output, status_markdown, live_symbol, live_pnl, live_details, live_chart, watchlist_output]
 
 
 
 
 
 
 
 
 
 
676
  )
677
 
678
  return demo
@@ -681,7 +777,7 @@ gradio_dashboard = create_gradio_ui()
681
  app = gr.mount_gradio_app(app, gradio_dashboard, path="/")
682
 
683
  # ==============================================================================
684
- # 🏁 تشغيل الخادم المدمج
685
  # ==============================================================================
686
  if __name__ == "__main__":
687
  import uvicorn
 
1
+ # app.py (V18.0 - GEM-Architect: Full Stack + UI Enhancements)
2
  import os
3
  import sys
4
  import traceback
 
492
  }
493
 
494
  # ==============================================================================
495
+ # 📊 [ 💡 GEM-ARCHITECT: NEW UI LOGIC ]
496
  # ==============================================================================
497
 
498
+ async def manual_close_current_trade():
499
+ """
500
+ دالة زر الإغلاق اليدوي
501
+ """
502
+ if not trade_manager.open_positions:
503
+ return "⚠️ لا توجد صفقة مفتوحة لإغلاقها."
504
+
505
+ symbol = list(trade_manager.open_positions.keys())[0]
506
+ await trade_manager.force_exit_by_manager(symbol, reason="MANUAL_UI_BUTTON")
507
+ return f"✅ تم إرسال أمر إغلاق فوري لـ {symbol}."
508
+
509
  async def check_live_pnl_and_status():
510
  """
511
+ تحديث الواجهة الدورية (الرسوم، الحالة، الإحصائيات، المحفظة)
 
512
  """
513
+ global trade_manager, data_manager, sys_state, r2
514
 
515
  empty_watchlist = pd.DataFrame(columns=["عملات المراقبة"])
516
+
517
+ # Default Values for Stats
518
+ wallet_md = "### 💰 جاري التحميل..."
519
+ history_md = "### 📊 جاري التحميل..."
520
 
521
  if not sys_state.ready:
522
+ return "النظام قيد التهيئة...", "...", "...", "...", "...", None, empty_watchlist, wallet_md, history_md
523
+
524
+ # 1. جلب بيانات المحفظة والإحصائيات من R2
525
+ try:
526
+ portfolio_state = await r2.get_portfolio_state_async()
527
+ current_capital = portfolio_state.get('current_capital_usd', 0.0)
528
+ total_trades = portfolio_state.get('total_trades', 0)
529
+ winning_trades = portfolio_state.get('winning_trades', 0)
530
+ total_profit = portfolio_state.get('total_profit_usd', 0.0)
531
+
532
+ win_rate = (winning_trades / total_trades * 100) if total_trades > 0 else 0.0
533
+
534
+ # Dynamic Wallet Calculation
535
+ dynamic_balance = current_capital
536
+ pnl_unrealized = 0.0
537
+
538
+ if trade_manager.open_positions:
539
+ symbol = list(trade_manager.open_positions.keys())[0]
540
+ trade = trade_manager.open_positions[symbol]
541
+ entry_price = float(trade.get('entry_price', 0.0))
542
+
543
+ curr_price = await data_manager.get_latest_price_async(symbol)
544
+ if curr_price > 0 and entry_price > 0:
545
+ # نفترض استثمار كامل رأس المال (للمحاكاة) أو نستخدم logic آخر
546
+ # هنا سنحسب نسبة الربح ونضيفها كرصيد وهمي للعرض
547
+ pnl_pct = (curr_price - entry_price) / entry_price
548
+ pnl_unrealized = current_capital * pnl_pct
549
+ dynamic_balance = current_capital + pnl_unrealized
550
+
551
+ # تنسيق عرض المحفظة
552
+ wallet_color = "#00ff00" if pnl_unrealized >= 0 else "#ff0000"
553
+ wallet_md = f"""
554
+ <div style='background-color: #1a1a1a; padding: 15px; border-radius: 8px; border: 1px solid #333;'>
555
+ <h3 style='margin-top:0; color: #888;'>💰 المحفظة الحية</h3>
556
+ <div style='font-size: 24px; font-weight: bold; color: white;'>
557
+ ${dynamic_balance:,.2f}
558
+ </div>
559
+ <div style='font-size: 14px; color: {wallet_color};'>
560
+ ({pnl_unrealized:+\,.2f} USD غير محقق)
561
+ </div>
562
+ <div style='font-size: 12px; color: #666; margin-top:5px;'>
563
+ الرصيد الأساسي: ${current_capital:,.2f}
564
+ </div>
565
+ </div>
566
+ """
567
 
568
+ # تنسيق عرض الإحصائيات
569
+ history_md = f"""
570
+ <div style='background-color: #1a1a1a; padding: 15px; border-radius: 8px; border: 1px solid #333;'>
571
+ <h3 style='margin-top:0; color: #888;'>📊 الأداء التاريخي</h3>
572
+ <table style='width:100%; color:white;'>
573
+ <tr><td>إجمالي الصفقات:</td><td style='text-align:left; font-weight:bold;'>{total_trades}</td></tr>
574
+ <tr><td>نسبة الفوز:</td><td style='text-align:left; font-weight:bold; color:#00ff00;'>{win_rate:.1f}%</td></tr>
575
+ <tr><td>الرابحة:</td><td style='text-align:left;'>{winning_trades}</td></tr>
576
+ <tr><td>صافي الربح:</td><td style='text-align:left; color: {"#00ff00" if total_profit>=0 else "#ff0000"};'>${total_profit:,.2f}</td></tr>
577
+ </table>
578
+ </div>
579
+ """
580
+
581
+ except Exception as e:
582
+ wallet_md = f"Error: {e}"
583
+ history_md = "Error loading stats"
584
+
585
+ # 2. الحالة العامة
586
  try:
587
  status_text = sys_state.last_cycle_logs
588
  trade_count = len(trade_manager.open_positions)
 
592
  **الصفقات المفتوحة:** {trade_count} |
593
  **قائمة المراقبة:** {watch_count}
594
  """
 
595
  watchlist_items = list(trade_manager.watchlist.keys())
596
  watchlist_df = pd.DataFrame(watchlist_items, columns=["عملات المراقبة"])
597
+ except Exception:
598
+ return "Error", "...", "...", "...", "...", None, empty_watchlist, wallet_md, history_md
 
599
 
600
+ # 3. التحقق من الصفقة المفتوحة (للشارت والـ PnL)
601
  if not trade_manager.open_positions:
602
+ return status_text, status_md, "### 💤 لا توجد صفقة مفتوحة", "---", "---", None, watchlist_df, wallet_md, history_md
603
 
604
  try:
605
  symbol = list(trade_manager.open_positions.keys())[0]
606
  trade = trade_manager.open_positions[symbol]
607
 
 
608
  current_price = await data_manager.get_latest_price_async(symbol)
 
 
609
  if current_price <= 0.0:
610
  current_price = float(trade.get('entry_price', 0.0))
611
  color = "gray"
 
613
  else:
614
  entry_price = float(trade['entry_price'])
615
  pnl_pct = ((current_price - entry_price) / entry_price) * 100
616
+ color = "#00ff00" if pnl_pct > 0 else "#ff0000"
617
+ pnl_text = f"{pnl_pct:+.2f}%"
 
 
 
 
 
 
 
 
 
618
 
619
  symbol_md = f"# {symbol}"
620
+ pnl_md = f"<div style='font-size: 40px; color:{color}; text-align:center; font-weight:bold;'>{pnl_text}</div>"
621
 
 
622
  entry_fmt = format_crypto_price(trade['entry_price'])
623
  curr_fmt = format_crypto_price(current_price)
624
  tp_fmt = format_crypto_price(trade['tp_price'])
 
626
 
627
  details_md = f"""
628
  <div style='background-color: #222; padding: 10px; border-radius: 5px;'>
629
+ <b>دخول:</b> {entry_fmt}<br>
630
+ <b>حالياً:</b> {curr_fmt}<br>
631
  <span style='color:#00ff00;'><b>TP:</b> {tp_fmt}</span><br>
632
  <span style='color:#ff0000;'><b>SL:</b> {sl_fmt}</span>
633
  </div>
634
  """
635
 
636
+ # Chart
637
  fig = None
638
  try:
639
  ohlcv = await data_manager.get_latest_ohlcv(symbol, '5m', 100)
640
  if ohlcv and len(ohlcv) > 0:
641
  df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
642
  df['datetime'] = pd.to_datetime(df['timestamp'], unit='ms')
 
643
  fig = plt.figure(figsize=(10, 5))
644
  ax = fig.add_subplot(111)
 
645
  fig.patch.set_facecolor('#0b0f19')
646
  ax.set_facecolor('#0b0f19')
 
647
  ax.plot(df['datetime'], df['close'], label=f'{symbol}', color='#00e5ff')
648
  ax.axhline(y=float(trade['entry_price']), color='white', linestyle='--', label='Entry')
649
  ax.axhline(y=float(trade['tp_price']), color='#00ff00', linestyle='-', label='TP')
650
  ax.axhline(y=float(trade['sl_price']), color='#ff0000', linestyle='-', label='SL')
 
651
  ax.tick_params(axis='x', colors='white')
652
  ax.tick_params(axis='y', colors='white')
653
  ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
654
  ax.legend(facecolor='#222', labelcolor='white')
 
655
  plt.tight_layout()
656
  else:
657
  fig = None
658
+ except Exception: fig = None
 
 
 
659
 
660
+ return status_text, status_md, symbol_md, pnl_md, details_md, fig, watchlist_df, wallet_md, history_md
661
 
662
  except Exception as e:
663
+ return status_text, status_md, f"Error: {e}", "...", "...", None, empty_watchlist, wallet_md, history_md
 
664
 
665
  async def run_cycle_from_gradio():
666
+ if sys_state.cycle_running: return "الدورة تعمل بالفعل."
 
667
  await run_unified_cycle()
668
  return sys_state.last_cycle_logs
669
 
 
671
  # 🚀 بناء وتثبيت واجهة Gradio
672
  # ==============================================================================
673
  def create_gradio_ui():
674
+ css = """
675
+ #pnl_md { text-align: center; }
676
+ .gradio-container { background-color: #0b0f19; }
677
+ """
678
 
679
+ with gr.Blocks(title="Titan V18 Pro Dashboard", css=css, theme=gr.themes.Monochrome()) as demo:
680
+ gr.Markdown("# 🚀 Titan V18 Pro Trading Terminal")
681
 
682
+ # --- الصف العلوي: الشارت + الإحصائيات الحية ---
683
  with gr.Row():
684
+ # العمود الأيسر: الشارت والمعلومات الفنية
685
  with gr.Column(scale=3):
686
+ live_chart = gr.Plot(label="Chart")
687
+ with gr.Row():
688
+ live_symbol = gr.Markdown("### No Active Trade")
689
+ live_pnl = gr.Markdown("---", elem_id="pnl_md")
690
+ live_details = gr.Markdown("Waiting for data...")
691
+
692
+ # العمود الأيمن: المحفظة والإحصائيات
693
  with gr.Column(scale=1):
694
+ wallet_output = gr.HTML(label="Wallet")
695
+ history_output = gr.HTML(label="Stats")
696
+ watchlist_output = gr.DataFrame(label="Watchlist")
697
 
698
  gr.HTML("<hr>")
699
 
700
+ # --- الصف السفلي: التحكم والسجلات ---
701
  with gr.Row():
702
  with gr.Column(scale=1):
703
+ gr.Markdown("## 🎮 التحكم (Controls)")
704
+
705
+ with gr.Row():
706
+ run_cycle_btn = gr.Button("🚀 (1) بدء الدورة (Scan)", variant="primary")
707
+ close_trade_btn = gr.Button("🚨 (2) إغلاق الصفقة (Panic)", variant="stop")
708
+
709
+ gr.Markdown("## 📡 حالة النظام")
710
+ status_markdown = gr.Markdown("جاري التهيئة...")
711
+
712
+ # رسائل التنبيه
713
+ alert_box = gr.Textbox(label="تنبيهات النظام", interactive=False)
714
 
715
  with gr.Column(scale=3):
716
+ gr.Markdown("## 📜 سجلات النظام (System Logs)")
717
  cycle_logs_output = gr.Textbox(
718
+ lines=15,
 
719
  autoscroll=True,
720
  max_lines=100,
721
+ value="..."
722
  )
723
 
724
+ # --- التفاعلات (Interactions) ---
725
+
726
+ # زر تشغيل الدورة
727
  run_cycle_btn.click(
728
  fn=run_cycle_from_gradio,
729
  inputs=None,
730
+ outputs=cycle_logs_output
 
731
  )
732
 
733
+ # زر الإغلاق اليدوي
734
+ close_trade_btn.click(
735
+ fn=manual_close_current_trade,
736
+ inputs=None,
737
+ outputs=alert_box
738
+ )
739
+
740
+ # التحديث الدوري (Timer)
741
  demo.load(
742
  fn=check_live_pnl_and_status,
743
  inputs=None,
744
+ outputs=[
745
+ cycle_logs_output,
746
+ status_markdown,
747
+ live_symbol,
748
+ live_pnl,
749
+ live_details,
750
+ live_chart,
751
+ watchlist_output,
752
+ wallet_output, # New
753
+ history_output # New
754
+ ]
755
  )
756
 
757
+ timer = gr.Timer(5) # تحديث كل 5 ثواني
 
758
  timer.tick(
759
  fn=check_live_pnl_and_status,
760
  inputs=None,
761
+ outputs=[
762
+ cycle_logs_output,
763
+ status_markdown,
764
+ live_symbol,
765
+ live_pnl,
766
+ live_details,
767
+ live_chart,
768
+ watchlist_output,
769
+ wallet_output, # New
770
+ history_output # New
771
+ ]
772
  )
773
 
774
  return demo
 
777
  app = gr.mount_gradio_app(app, gradio_dashboard, path="/")
778
 
779
  # ==============================================================================
780
+ # 🏁 تشغيل الخادم
781
  # ==============================================================================
782
  if __name__ == "__main__":
783
  import uvicorn