Files
XauBot/archive/dashboard_gui.py
T
GifariKemal 0d25548ed5 refactor: restructure repository and add README, CLAUDE.md, LICENSE
- Move utility scripts to scripts/ (check_market, check_positions, etc.)
- Move test files to tests/ (test_modules, test_mt5_connection, etc.)
- Move deprecated dashboards to archive/
- Move research files to docs/research/
- Add sys.path fix to all moved Python files
- Rewrite README.md with architecture diagram and badges
- Add CLAUDE.md project guide
- Add MIT LICENSE
- Update .gitignore with archive/ pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 13:22:46 +07:00

650 lines
25 KiB
Python

"""
Trading Bot Dashboard - Live Monitoring GUI
============================================
Real-time view of SMC, ML, Market conditions, and system status.
"""
import tkinter as tk
from tkinter import ttk, scrolledtext
import threading
import time
from datetime import datetime
from zoneinfo import ZoneInfo
import json
from pathlib import Path
# Add project to path
import sys
sys.path.insert(0, str(Path(__file__).parent))
# Load environment variables
from dotenv import load_dotenv
load_dotenv()
# Import bot components
try:
from src.mt5_connector import MT5Connector
from src.smc_polars import SMCAnalyzer, SMCSignal
from src.ml_model import TradingModel, PredictionResult
from src.regime_detector import MarketRegimeDetector, RegimeState, MarketRegime
from src.session_filter import SessionFilter
from src.feature_eng import FeatureEngineer
from src.smart_risk_manager import SmartRiskManager
from src.config import TradingConfig
except ImportError as e:
print(f"Import error: {e}")
print("Make sure you're running from the project directory")
sys.exit(1)
class TradingDashboard:
def __init__(self, root):
self.root = root
self.root.title("Trading Bot Dashboard - Live Monitor")
self.root.geometry("1200x800")
self.root.configure(bg='#1a1a2e')
# Initialize components
self.mt5 = None
self.smc = None
self.ml = None
self.hmm = None
self.analyzer = None
self.feature_eng = None
self.session = None
self.config = TradingConfig()
# State
self.running = False
self.last_update = None
# Setup UI
self.setup_styles()
self.create_widgets()
# Start connection
self.connect_systems()
def setup_styles(self):
"""Setup custom styles"""
style = ttk.Style()
style.theme_use('clam')
# Configure colors
style.configure('Dashboard.TFrame', background='#1a1a2e')
style.configure('Card.TFrame', background='#16213e')
style.configure('Header.TLabel',
background='#1a1a2e',
foreground='#e94560',
font=('Segoe UI', 16, 'bold'))
style.configure('CardTitle.TLabel',
background='#16213e',
foreground='#00d9ff',
font=('Segoe UI', 11, 'bold'))
style.configure('Value.TLabel',
background='#16213e',
foreground='#ffffff',
font=('Consolas', 12))
style.configure('ValueBig.TLabel',
background='#16213e',
foreground='#00ff88',
font=('Consolas', 18, 'bold'))
style.configure('Buy.TLabel',
background='#16213e',
foreground='#00ff88',
font=('Consolas', 14, 'bold'))
style.configure('Sell.TLabel',
background='#16213e',
foreground='#ff4757',
font=('Consolas', 14, 'bold'))
style.configure('Hold.TLabel',
background='#16213e',
foreground='#ffa502',
font=('Consolas', 14, 'bold'))
def create_widgets(self):
"""Create all dashboard widgets"""
# Main container
main_frame = ttk.Frame(self.root, style='Dashboard.TFrame')
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Header
header = ttk.Label(main_frame, text="🤖 TRADING BOT DASHBOARD", style='Header.TLabel')
header.pack(pady=(0, 10))
# Top row - Price and Account
top_frame = ttk.Frame(main_frame, style='Dashboard.TFrame')
top_frame.pack(fill=tk.X, pady=5)
self.create_price_card(top_frame)
self.create_account_card(top_frame)
self.create_session_card(top_frame)
# Middle row - Signals
mid_frame = ttk.Frame(main_frame, style='Dashboard.TFrame')
mid_frame.pack(fill=tk.X, pady=5)
self.create_smc_card(mid_frame)
self.create_ml_card(mid_frame)
self.create_regime_card(mid_frame)
# Third row - Risk and Positions
third_frame = ttk.Frame(main_frame, style='Dashboard.TFrame')
third_frame.pack(fill=tk.X, pady=5)
self.create_risk_card(third_frame)
self.create_positions_card(third_frame)
# Bottom - Settings and Log
bottom_frame = ttk.Frame(main_frame, style='Dashboard.TFrame')
bottom_frame.pack(fill=tk.BOTH, expand=True, pady=5)
self.create_settings_card(bottom_frame)
self.create_log_card(bottom_frame)
# Status bar
self.status_var = tk.StringVar(value="Connecting...")
status_bar = ttk.Label(main_frame, textvariable=self.status_var,
background='#0f3460', foreground='#ffffff',
font=('Segoe UI', 9))
status_bar.pack(fill=tk.X, pady=(5, 0))
def create_card(self, parent, title, width=280):
"""Create a styled card frame"""
card = tk.Frame(parent, bg='#16213e', bd=1, relief=tk.RAISED)
card.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.BOTH, expand=True)
title_label = ttk.Label(card, text=title, style='CardTitle.TLabel')
title_label.pack(pady=(10, 5), padx=10, anchor='w')
content = tk.Frame(card, bg='#16213e')
content.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))
return content
def create_price_card(self, parent):
"""Price information card"""
content = self.create_card(parent, "📊 PRICE")
self.price_var = tk.StringVar(value="---.--")
price_label = tk.Label(content, textvariable=self.price_var,
bg='#16213e', fg='#00ff88',
font=('Consolas', 24, 'bold'))
price_label.pack(pady=5)
self.spread_var = tk.StringVar(value="Spread: -- pips")
spread_label = tk.Label(content, textvariable=self.spread_var,
bg='#16213e', fg='#888888',
font=('Consolas', 10))
spread_label.pack()
self.time_var = tk.StringVar(value="--:--:--")
time_label = tk.Label(content, textvariable=self.time_var,
bg='#16213e', fg='#aaaaaa',
font=('Consolas', 10))
time_label.pack()
def create_account_card(self, parent):
"""Account information card"""
content = self.create_card(parent, "💰 ACCOUNT")
self.balance_var = tk.StringVar(value="Balance: $---.--")
balance_label = tk.Label(content, textvariable=self.balance_var,
bg='#16213e', fg='#ffffff',
font=('Consolas', 12))
balance_label.pack(anchor='w', pady=2)
self.equity_var = tk.StringVar(value="Equity: $---.--")
equity_label = tk.Label(content, textvariable=self.equity_var,
bg='#16213e', fg='#ffffff',
font=('Consolas', 12))
equity_label.pack(anchor='w', pady=2)
self.profit_var = tk.StringVar(value="P/L: $0.00")
profit_label = tk.Label(content, textvariable=self.profit_var,
bg='#16213e', fg='#00ff88',
font=('Consolas', 12, 'bold'))
profit_label.pack(anchor='w', pady=2)
def create_session_card(self, parent):
"""Session information card"""
content = self.create_card(parent, "🕐 SESSION")
self.session_var = tk.StringVar(value="Loading...")
session_label = tk.Label(content, textvariable=self.session_var,
bg='#16213e', fg='#ffa502',
font=('Consolas', 11, 'bold'))
session_label.pack(anchor='w', pady=2)
self.golden_var = tk.StringVar(value="Golden Time: --")
golden_label = tk.Label(content, textvariable=self.golden_var,
bg='#16213e', fg='#ffcc00',
font=('Consolas', 11))
golden_label.pack(anchor='w', pady=2)
self.can_trade_var = tk.StringVar(value="Can Trade: --")
can_trade_label = tk.Label(content, textvariable=self.can_trade_var,
bg='#16213e', fg='#00ff88',
font=('Consolas', 11))
can_trade_label.pack(anchor='w', pady=2)
def create_smc_card(self, parent):
"""SMC Signal card"""
content = self.create_card(parent, "📈 SMC SIGNAL")
self.smc_signal_var = tk.StringVar(value="---")
self.smc_signal_label = tk.Label(content, textvariable=self.smc_signal_var,
bg='#16213e', fg='#888888',
font=('Consolas', 20, 'bold'))
self.smc_signal_label.pack(pady=5)
self.smc_conf_var = tk.StringVar(value="Confidence: --%")
smc_conf_label = tk.Label(content, textvariable=self.smc_conf_var,
bg='#16213e', fg='#aaaaaa',
font=('Consolas', 10))
smc_conf_label.pack()
self.smc_reason_var = tk.StringVar(value="")
smc_reason_label = tk.Label(content, textvariable=self.smc_reason_var,
bg='#16213e', fg='#666666',
font=('Consolas', 9), wraplength=250)
smc_reason_label.pack(pady=5)
def create_ml_card(self, parent):
"""ML Prediction card"""
content = self.create_card(parent, "🤖 ML PREDICTION")
self.ml_signal_var = tk.StringVar(value="---")
self.ml_signal_label = tk.Label(content, textvariable=self.ml_signal_var,
bg='#16213e', fg='#888888',
font=('Consolas', 20, 'bold'))
self.ml_signal_label.pack(pady=5)
self.ml_conf_var = tk.StringVar(value="Confidence: --%")
ml_conf_label = tk.Label(content, textvariable=self.ml_conf_var,
bg='#16213e', fg='#aaaaaa',
font=('Consolas', 10))
ml_conf_label.pack()
self.ml_prob_var = tk.StringVar(value="Buy: --% | Sell: --%")
ml_prob_label = tk.Label(content, textvariable=self.ml_prob_var,
bg='#16213e', fg='#666666',
font=('Consolas', 9))
ml_prob_label.pack(pady=5)
def create_regime_card(self, parent):
"""Market Regime card"""
content = self.create_card(parent, "🌊 MARKET REGIME")
self.regime_var = tk.StringVar(value="---")
self.regime_label = tk.Label(content, textvariable=self.regime_var,
bg='#16213e', fg='#888888',
font=('Consolas', 14, 'bold'))
self.regime_label.pack(pady=5)
self.volatility_var = tk.StringVar(value="Volatility: --")
vol_label = tk.Label(content, textvariable=self.volatility_var,
bg='#16213e', fg='#aaaaaa',
font=('Consolas', 10))
vol_label.pack()
self.atr_var = tk.StringVar(value="ATR: --")
atr_label = tk.Label(content, textvariable=self.atr_var,
bg='#16213e', fg='#666666',
font=('Consolas', 9))
atr_label.pack(pady=5)
def create_risk_card(self, parent):
"""Risk Management card"""
content = self.create_card(parent, "⚠️ RISK STATUS")
self.daily_loss_var = tk.StringVar(value="Daily Loss: $0.00")
daily_loss_label = tk.Label(content, textvariable=self.daily_loss_var,
bg='#16213e', fg='#ff4757',
font=('Consolas', 11))
daily_loss_label.pack(anchor='w', pady=2)
self.daily_profit_var = tk.StringVar(value="Daily Profit: $0.00")
daily_profit_label = tk.Label(content, textvariable=self.daily_profit_var,
bg='#16213e', fg='#00ff88',
font=('Consolas', 11))
daily_profit_label.pack(anchor='w', pady=2)
self.total_loss_var = tk.StringVar(value="Total Loss: $0.00")
total_loss_label = tk.Label(content, textvariable=self.total_loss_var,
bg='#16213e', fg='#ffa502',
font=('Consolas', 11))
total_loss_label.pack(anchor='w', pady=2)
self.consec_loss_var = tk.StringVar(value="Consecutive Losses: 0")
consec_label = tk.Label(content, textvariable=self.consec_loss_var,
bg='#16213e', fg='#aaaaaa',
font=('Consolas', 10))
consec_label.pack(anchor='w', pady=2)
def create_positions_card(self, parent):
"""Open Positions card"""
card = tk.Frame(parent, bg='#16213e', bd=1, relief=tk.RAISED)
card.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.BOTH, expand=True)
title_label = ttk.Label(card, text="📋 OPEN POSITIONS", style='CardTitle.TLabel')
title_label.pack(pady=(10, 5), padx=10, anchor='w')
# Positions listbox
self.positions_text = tk.Text(card, height=5, bg='#0f3460', fg='#ffffff',
font=('Consolas', 9), bd=0)
self.positions_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))
self.positions_text.insert('1.0', "No open positions")
self.positions_text.config(state=tk.DISABLED)
def create_settings_card(self, parent):
"""Settings display card"""
card = tk.Frame(parent, bg='#16213e', bd=1, relief=tk.RAISED, width=350)
card.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.BOTH)
card.pack_propagate(False)
title_label = ttk.Label(card, text="⚙️ SETTINGS", style='CardTitle.TLabel')
title_label.pack(pady=(10, 5), padx=10, anchor='w')
content = tk.Frame(card, bg='#16213e')
content.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))
settings_info = [
("Symbol:", self.config.symbol),
("Capital:", f"${self.config.capital:,.2f}"),
("Max Daily Loss:", f"{self.config.risk.max_daily_loss}%"),
("Risk Per Trade:", f"{self.config.risk.risk_per_trade}%"),
("Min Lot:", f"{self.config.risk.min_lot_size}"),
("Max Lot:", f"{self.config.risk.max_lot_size}"),
("Timeframe:", self.config.execution_timeframe),
("Golden Time:", "19:00-23:00 WIB"),
]
for label, value in settings_info:
row = tk.Frame(content, bg='#16213e')
row.pack(fill=tk.X, pady=1)
lbl = tk.Label(row, text=label, bg='#16213e', fg='#888888',
font=('Consolas', 9), width=15, anchor='w')
lbl.pack(side=tk.LEFT)
val = tk.Label(row, text=value, bg='#16213e', fg='#ffffff',
font=('Consolas', 9), anchor='w')
val.pack(side=tk.LEFT)
def create_log_card(self, parent):
"""Activity log card"""
card = tk.Frame(parent, bg='#16213e', bd=1, relief=tk.RAISED)
card.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.BOTH, expand=True)
title_label = ttk.Label(card, text="📝 ACTIVITY LOG", style='CardTitle.TLabel')
title_label.pack(pady=(10, 5), padx=10, anchor='w')
self.log_text = scrolledtext.ScrolledText(card, height=8, bg='#0f3460', fg='#00ff88',
font=('Consolas', 9), bd=0)
self.log_text.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))
def log(self, message):
"""Add message to activity log"""
timestamp = datetime.now().strftime("%H:%M:%S")
self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
self.log_text.see(tk.END)
def connect_systems(self):
"""Connect to MT5 and initialize components"""
self.log("Connecting to MT5...")
try:
self.mt5 = MT5Connector(
login=self.config.mt5_login,
password=self.config.mt5_password,
server=self.config.mt5_server,
path=self.config.mt5_path,
)
if not self.mt5.connect():
self.log("ERROR: Failed to connect to MT5!")
return
self.log("MT5 connected successfully!")
# Initialize components
self.smc = SMCAnalyzer()
self.ml = TradingModel(model_path="models/xgboost_model")
self.ml.load() # Load saved model
self.hmm = MarketRegimeDetector(model_path="models/hmm_regime")
self.hmm.load() # Load saved regime model
self.session = SessionFilter()
self.feature_eng = FeatureEngineer()
self.log("All components initialized")
if self.ml.fitted:
self.log("ML Model loaded successfully")
else:
self.log("WARNING: ML Model not fitted")
self.running = True
# Start update thread
update_thread = threading.Thread(target=self.update_loop, daemon=True)
update_thread.start()
except Exception as e:
self.log(f"ERROR: {e}")
def update_loop(self):
"""Main update loop running in background thread"""
while self.running:
try:
self.update_data()
time.sleep(1) # Update every second
except Exception as e:
self.log(f"Update error: {e}")
time.sleep(5)
def update_data(self):
"""Fetch and update all data"""
if not self.mt5:
return
# Get current time
wib = ZoneInfo("Asia/Jakarta")
now = datetime.now(wib)
self.time_var.set(now.strftime("%H:%M:%S WIB"))
# Check golden time
is_golden = 19 <= now.hour <= 23
self.golden_var.set(f"Golden Time: {'YES 🌟' if is_golden else 'NO'}")
# Get price data
try:
tick = self.mt5.get_tick(self.config.symbol)
if tick:
price = (tick.bid + tick.ask) / 2
spread = (tick.ask - tick.bid) * 100 # in pips
self.price_var.set(f"{price:.2f}")
self.spread_var.set(f"Spread: {spread:.1f} pips")
except:
pass
# Get account info
try:
balance = self.mt5.account_balance
equity = self.mt5.account_equity
profit = equity - balance
self.balance_var.set(f"Balance: ${balance:,.2f}")
self.equity_var.set(f"Equity: ${equity:,.2f}")
self.profit_var.set(f"P/L: ${profit:+,.2f}")
except:
pass
# Get session info
try:
session_info = self.session.get_status_report()
if session_info:
current_session = session_info.get('current_session', 'Unknown')
self.session_var.set(current_session)
can_trade, reason, mult = self.session.can_trade()
self.can_trade_var.set(f"Can Trade: {'YES ✓' if can_trade else 'NO ✗'}")
except:
pass
# Get market data for analysis
try:
df = self.mt5.get_market_data(self.config.symbol, self.config.execution_timeframe, 500)
if df is not None and len(df) > 100:
self.update_signals(df)
except Exception as e:
self.log(f"Data error: {e}")
# Get positions
try:
positions = self.mt5.get_open_positions(self.config.symbol)
self.update_positions(positions)
except:
pass
# Update risk state
self.update_risk_state()
# Update status
self.last_update = datetime.now()
self.status_var.set(f"Last update: {self.last_update.strftime('%H:%M:%S')} | Running...")
def update_signals(self, df):
"""Update SMC and ML signals"""
# Build complete feature DataFrame (same as main_live.py)
# 1. Technical features
df = self.feature_eng.calculate_all(df, include_ml_features=True)
# 2. SMC features
df = self.smc.calculate_all(df)
# 3. Regime detection
try:
df = self.hmm.predict(df)
regime = self.hmm.get_current_state(df)
if regime:
regime_name = regime.regime.value.replace('_', ' ').title()
self.regime_var.set(regime_name)
self.volatility_var.set(f"Volatility: {regime.volatility:.2f}")
self.atr_var.set(f"Conf: {regime.confidence:.0%}")
# Update color based on regime
if "HIGH" in regime.regime.value:
self.regime_label.config(fg='#ff4757')
elif "LOW" in regime.regime.value:
self.regime_label.config(fg='#00ff88')
else:
self.regime_label.config(fg='#ffa502')
except Exception as e:
pass
# 4. SMC Signal
try:
smc_signal = self.smc.generate_signal(df)
if smc_signal:
signal_type = smc_signal.signal_type
self.smc_signal_var.set(signal_type)
self.smc_conf_var.set(f"Confidence: {smc_signal.confidence:.0%}")
self.smc_reason_var.set(smc_signal.reason[:50] + "..." if len(smc_signal.reason) > 50 else smc_signal.reason)
# Update color
if signal_type == "BUY":
self.smc_signal_label.config(fg='#00ff88')
elif signal_type == "SELL":
self.smc_signal_label.config(fg='#ff4757')
else:
self.smc_signal_var.set("NO SIGNAL")
self.smc_signal_label.config(fg='#888888')
self.smc_conf_var.set("Confidence: --%")
self.smc_reason_var.set("")
except:
pass
# 5. ML Prediction (with all features now available)
try:
if self.ml.fitted:
# Get available features that match model's expected features
available_features = [f for f in self.ml.feature_names if f in df.columns]
ml_pred = self.ml.predict(df, available_features)
if ml_pred:
signal = ml_pred.signal
self.ml_signal_var.set(signal)
self.ml_conf_var.set(f"Confidence: {ml_pred.confidence:.0%}")
# probability is for BUY, so sell_prob = 1 - probability
buy_prob = ml_pred.probability
sell_prob = 1.0 - buy_prob
self.ml_prob_var.set(f"Buy: {buy_prob:.0%} | Sell: {sell_prob:.0%}")
# Update color
if signal == "BUY":
self.ml_signal_label.config(fg='#00ff88')
elif signal == "SELL":
self.ml_signal_label.config(fg='#ff4757')
else:
self.ml_signal_label.config(fg='#ffa502')
except:
pass
def update_positions(self, positions):
"""Update positions display (positions is a Polars DataFrame)"""
self.positions_text.config(state=tk.NORMAL)
self.positions_text.delete('1.0', tk.END)
if positions is None or positions.is_empty():
self.positions_text.insert('1.0', "No open positions")
else:
for row in positions.iter_rows(named=True):
ticket = row.get('ticket', 'N/A')
pos_type = "BUY" if row.get('type', 0) == 0 else "SELL"
volume = row.get('volume', 0)
profit = row.get('profit', 0)
price_open = row.get('price_open', 0)
line = f"#{ticket} | {pos_type} {volume} @ {price_open:.2f} | P/L: ${profit:+.2f}\n"
self.positions_text.insert(tk.END, line)
self.positions_text.config(state=tk.DISABLED)
def update_risk_state(self):
"""Update risk state from file"""
try:
risk_file = Path("data/risk_state.txt")
if risk_file.exists():
content = risk_file.read_text()
lines = content.strip().split('\n')
for line in lines:
if ':' in line:
key, value = line.split(':', 1)
key = key.strip()
value = value.strip()
if key == 'daily_loss':
self.daily_loss_var.set(f"Daily Loss: ${float(value):,.2f}")
elif key == 'daily_profit':
self.daily_profit_var.set(f"Daily Profit: ${float(value):,.2f}")
elif key == 'total_loss':
self.total_loss_var.set(f"Total Loss: ${float(value):,.2f}")
elif key == 'consecutive_losses':
self.consec_loss_var.set(f"Consecutive Losses: {value}")
except:
pass
def on_closing(self):
"""Handle window close"""
self.running = False
if self.mt5:
self.mt5.disconnect()
self.root.destroy()
def main():
root = tk.Tk()
app = TradingDashboard(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
root.mainloop()
if __name__ == "__main__":
main()