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>
This commit is contained in:
GifariKemal
2026-02-06 13:22:46 +07:00
co-authored by Claude Opus 4.6
parent 20dc1385c3
commit 0d25548ed5
25 changed files with 364 additions and 82 deletions
Binary file not shown.
+186
View File
@@ -0,0 +1,186 @@
"""
Create Excel comparison report for trading systems
"""
import openpyxl
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
wb = openpyxl.Workbook()
ws = wb.active
ws.title = 'System Comparison'
# Styles
header_font = Font(bold=True, color='FFFFFF', size=11)
header_fill = PatternFill('solid', fgColor='2F5496')
category_fill = PatternFill('solid', fgColor='D9E2F3')
winner_fill = PatternFill('solid', fgColor='C6EFCE')
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# Headers
headers = ['Feature / Criteria', 'Smart Trading BOT + AI', 'Forex_SMC_AI_Bot', 'GBPUSD H1 QuadLayer', 'RSI v3.7 Optimized', 'Winner']
for col, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col, value=header)
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal='center', wrap_text=True)
cell.border = thin_border
# Data rows
data = [
# Basic Info
('BASIC INFO', '', '', '', '', ''),
('Trading Pair', 'XAUUSD (Gold)', 'XAUUSD+', 'GBPUSD', 'GBPUSD', 'Smart BOT'),
('Timeframe', 'M15 + H4 MTF', 'M15', 'H1', 'H1', 'Smart BOT'),
('Strategy Type', 'Hybrid AI + SMC', 'SMC Only', 'Order Block + Quality', 'RSI Mean Reversion', 'Smart BOT'),
('Code Lines', '~10,000', '~100', '~1,900', '~1,000', 'Smart BOT'),
('Data Framework', 'Polars (10-50x faster)', 'Pandas', 'Pandas', 'Pandas', 'Smart BOT'),
# Machine Learning
('MACHINE LEARNING', '', '', '', '', ''),
('ML Model', 'XGBoost (37 features)', 'None', 'None', 'None', 'Smart BOT'),
('Regime Detection', 'HMM (3 states)', 'None', 'EMA-based', 'SMA-based', 'Smart BOT'),
('Auto-Training', 'Daily (walk-forward)', 'No', 'No', 'No', 'Smart BOT'),
('Dynamic Thresholds', 'Yes (market-adaptive)', 'No', 'Quality score', 'No', 'Smart BOT'),
('Feature Engineering', '37+ features', 'None', 'Basic (ATR, ADX)', 'RSI, ATR', 'Smart BOT'),
# Smart Money Concepts
('SMART MONEY CONCEPTS', '', '', '', '', ''),
('FVG Detection', 'Yes (Pure Polars)', 'Yes (library)', 'Yes', 'No', 'Smart BOT'),
('Order Blocks', 'Yes (Pure Polars)', 'Yes (library)', 'Yes', 'No', 'Smart BOT'),
('BOS/CHoCH', 'Yes', 'No', 'No', 'No', 'Smart BOT'),
('Liquidity Zones', 'Yes (native)', 'No', 'No', 'No', 'Smart BOT'),
('Swing Points', 'Yes', 'Yes', 'No', 'No', 'Smart BOT'),
('SMC Implementation', 'Native (no library)', 'External library', 'Custom', 'N/A', 'Smart BOT'),
# Risk Management
('RISK MANAGEMENT', '', '', '', '', ''),
('Daily Loss Limit', '5%', 'None', 'Layer 3 (P&L)', '3%', 'Smart BOT'),
('Total Loss Limit', '10%', 'None', 'Monthly stop', 'None', 'Smart BOT'),
('Per-Trade Loss', '1% ($50)', 'None', '0.15%', '1%', 'Smart BOT'),
('Position Sizing', 'Half-Kelly + Regime', 'Fixed', 'ATR-based', 'Risk %', 'Smart BOT'),
('Hard Stop Loss', 'NO (smart exit)', 'Unknown', 'Yes (ATR*1.5)', 'Yes (ATR*1.5)', 'Smart BOT'),
('Flash Crash Protection', 'Yes (2.5% trigger)', 'No', 'No', 'No', 'Smart BOT'),
('Weekend Protection', 'Yes', 'No', 'No', 'No', 'Smart BOT'),
# Architecture
('ARCHITECTURE', '', '', '', '', ''),
('Async Processing', 'Yes (asyncio)', 'No', 'Yes', 'Yes', 'Tie'),
('Auto-Reconnect', 'Yes (with retry)', 'No', 'Basic', 'Basic', 'Smart BOT'),
('Modular Design', '15+ modules', '3 files', '10+ files', '1 file', 'Smart BOT'),
('External Dependencies', 'Minimal', 'SMC library', 'PostgreSQL, Redis', 'Minimal', 'Smart BOT'),
('Database Required', 'No', 'No', 'Yes (PostgreSQL)', 'No', 'Smart BOT'),
# Signal Generation
('SIGNAL GENERATION', '', '', '', '', ''),
('Entry Confirmation', 'SMC + ML Agreement', 'SMC only', '4-Layer Quality', 'RSI thresholds', 'Smart BOT'),
('Multi-Timeframe', 'Yes (M15 + H4)', 'Single', 'Single', 'Single', 'Smart BOT'),
('Signal Confluence', 'FVG + OB + ML', 'FVG + OB', 'OB + Quality', 'RSI only', 'Smart BOT'),
# Session Management
('SESSION MANAGEMENT', '', '', '', '', ''),
('Timezone Support', 'WIB (GMT+7)', 'None', 'UTC', 'UTC', 'Smart BOT'),
('Session Filter', 'Sydney/Tokyo/London/NY', 'None', 'Kill Zones', 'Hour-based', 'Smart BOT'),
('Session Multiplier', 'Yes (0.5x-1.2x)', 'No', 'Hour multipliers', 'No', 'Smart BOT'),
# Notifications
('NOTIFICATIONS', '', '', '', '', ''),
('Telegram Integration', 'Full (charts, alerts)', 'No', 'Full (commands)', 'Full', 'Tie'),
('Telegram Commands', 'Basic', 'None', '20+ commands', '10+ commands', 'QuadLayer'),
('Trade Alerts', 'Yes (detailed)', 'No', 'Yes', 'Yes', 'Smart BOT'),
# Performance
('BACKTEST PERFORMANCE', '', '', '', '', ''),
('Win Rate', '62-68%', 'Unknown', '45.3%', '37.6%', 'Smart BOT'),
('Profit Factor', '2.0-2.5', 'Unknown', '4.18', '~1.8', 'QuadLayer'),
('Max Drawdown', '2-3%', 'Unknown', '0.75%', '14.4%', 'QuadLayer'),
('Losing Months', 'Unknown', 'Unknown', '0/13', '2/16', 'QuadLayer'),
# Unique Features
('UNIQUE FEATURES', '', '', '', '', ''),
('AI/ML Integration', 'XGBoost + HMM', 'None', 'None', 'None', 'Smart BOT'),
('News Agent', 'Yes (calendar)', 'No', 'No', 'No', 'Smart BOT'),
('Smart Position Guard', 'Yes (momentum, TP prob)', 'No', 'No', 'No', 'Smart BOT'),
('Recovery Mode', 'Yes (0.5x lot)', 'No', 'Yes', 'Yes (cooldown)', 'Smart BOT'),
('Pattern Filter', 'ML-based', 'No', 'Yes (Layer 4)', 'Regime filter', 'Smart BOT'),
('Vector DB Support', 'No', 'No', 'Yes (Qdrant)', 'No', 'QuadLayer'),
]
row = 2
for item in data:
for col, value in enumerate(item, 1):
cell = ws.cell(row=row, column=col, value=value)
cell.border = thin_border
cell.alignment = Alignment(wrap_text=True, vertical='center')
# Category rows
if item[1] == '' and item[0].isupper():
cell.fill = category_fill
cell.font = Font(bold=True)
# Winner column highlighting
if col == 6 and value == 'Smart BOT':
cell.fill = winner_fill
cell.font = Font(bold=True, color='006100')
row += 1
# Set column widths
ws.column_dimensions['A'].width = 25
ws.column_dimensions['B'].width = 30
ws.column_dimensions['C'].width = 22
ws.column_dimensions['D'].width = 25
ws.column_dimensions['E'].width = 22
ws.column_dimensions['F'].width = 12
# Freeze header row
ws.freeze_panes = 'A2'
# Add Summary sheet
ws2 = wb.create_sheet('Summary')
summary_data = [
('SYSTEM COMPARISON SUMMARY', ''),
('', ''),
('Total Comparison Categories', '50+'),
('', ''),
('WINNER COUNT:', ''),
('Smart Trading BOT + AI (Ours)', '42 categories'),
('GBPUSD H1 QuadLayer', '5 categories'),
('RSI v3.7 Optimized', '0 categories'),
('Forex_SMC_AI_Bot', '0 categories'),
('Tie', '3 categories'),
('', ''),
('KEY ADVANTAGES OF SMART BOT:', ''),
('1. AI/ML Integration', 'XGBoost + HMM (unique)'),
('2. Data Performance', 'Polars (10-50x faster)'),
('3. SMC Implementation', 'Native (no external lib)'),
('4. Risk Management', 'Most comprehensive'),
('5. Multi-Timeframe', 'M15 + H4 analysis'),
('6. Auto-Training', 'Daily model updates'),
('7. Win Rate', '62-68% (highest)'),
('8. Mental Health Focus', 'No hard SL, ultra-safe lots'),
('', ''),
('CONCLUSION:', ''),
('', 'Smart Trading BOT + AI is SIGNIFICANTLY'),
('', 'more advanced than all compared systems.'),
('', 'It combines AI/ML with SMC in a unique way'),
('', 'that no other system has.'),
]
for row_num, (key, value) in enumerate(summary_data, 1):
ws2.cell(row=row_num, column=1, value=key)
ws2.cell(row=row_num, column=2, value=value)
if 'WINNER' in key or 'KEY ADVANTAGES' in key or 'CONCLUSION' in key:
ws2.cell(row=row_num, column=1).font = Font(bold=True)
if 'Smart Trading BOT' in key:
ws2.cell(row=row_num, column=1).fill = winner_fill
ws2.cell(row=row_num, column=2).fill = winner_fill
ws2.column_dimensions['A'].width = 35
ws2.column_dimensions['B'].width = 40
wb.save('comparison_report.xlsx')
print('Excel file created successfully: comparison_report.xlsx')
+649
View File
@@ -0,0 +1,649 @@
"""
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()
+907
View File
@@ -0,0 +1,907 @@
"""
Trading Bot Dashboard - Modern UI with CustomTkinter
=====================================================
Beautiful responsive dashboard with dark/light mode toggle.
"""
import customtkinter as ctk
import threading
import time
from datetime import datetime
from zoneinfo import ZoneInfo
from pathlib import Path
import sys
# Add project to path
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
from src.ml_model import TradingModel
from src.regime_detector import MarketRegimeDetector
from src.session_filter import SessionFilter
from src.feature_eng import FeatureEngineer
from src.config import TradingConfig
except ImportError as e:
print(f"Import error: {e}")
sys.exit(1)
# Set default appearance
ctk.set_default_color_theme("blue")
# Theme colors
THEMES = {
"dark": {
"bg": "#0d0d1a",
"card": "#1a1a2e",
"card_inner": "#0d0d1a",
"text": "#ffffff",
"text_secondary": "#888888",
"text_muted": "#666666",
"accent": "#00d4ff",
"green": "#00ff88",
"red": "#ff4757",
"orange": "#ffa500",
"highlight": "#2d4a2d",
"highlight_off": "#2d2d44",
},
"light": {
"bg": "#f0f2f5",
"card": "#ffffff",
"card_inner": "#f8f9fa",
"text": "#1a1a2e",
"text_secondary": "#555555",
"text_muted": "#888888",
"accent": "#0066cc",
"green": "#00aa55",
"red": "#dd3344",
"orange": "#ee8800",
"highlight": "#d4edda",
"highlight_off": "#e9ecef",
}
}
class ModernCard(ctk.CTkFrame):
"""Reusable card component with title - theme aware"""
def __init__(self, master, title, theme="dark", **kwargs):
self.theme = theme
colors = THEMES[theme]
super().__init__(master, corner_radius=12, fg_color=colors["card"], **kwargs)
# Title
self.title_label = ctk.CTkLabel(
self,
text=title,
font=ctk.CTkFont(size=12, weight="bold"),
text_color=colors["accent"]
)
self.title_label.pack(anchor="w", padx=12, pady=(10, 6))
# Content frame
self.content = ctk.CTkFrame(self, fg_color="transparent")
self.content.pack(fill="both", expand=True, padx=12, pady=(0, 10))
def update_theme(self, theme):
"""Update card colors for theme"""
self.theme = theme
colors = THEMES[theme]
self.configure(fg_color=colors["card"])
self.title_label.configure(text_color=colors["accent"])
class TradingDashboard(ctk.CTk):
"""Modern Trading Dashboard with CustomTkinter - Responsive & Theme Toggle"""
def __init__(self):
super().__init__()
# Theme state
self.current_theme = "dark"
ctk.set_appearance_mode("dark")
# Window setup - responsive minimum size for split screen
self.title("AI Trading Bot")
self.geometry("700x800")
self.minsize(600, 700)
self.configure(fg_color=THEMES[self.current_theme]["bg"])
# Store all themed widgets for updates
self.themed_widgets = []
self.cards = []
# Initialize components
self.mt5 = None
self.smc = None
self.ml = None
self.hmm = None
self.session = None
self.feature_eng = None
self.config = TradingConfig()
# State
self.running = False
self.last_update = None
# Create UI
self.create_header()
self.create_main_layout()
self.create_status_bar()
# Start connection
self.after(100, self.connect_systems)
def toggle_theme(self):
"""Toggle between dark and light mode"""
self.current_theme = "light" if self.current_theme == "dark" else "dark"
ctk.set_appearance_mode(self.current_theme)
self.apply_theme()
def apply_theme(self):
"""Apply current theme to all widgets"""
colors = THEMES[self.current_theme]
# Update main window
self.configure(fg_color=colors["bg"])
# Update theme button
if self.current_theme == "dark":
self.theme_btn.configure(text="☀️ Light")
else:
self.theme_btn.configure(text="🌙 Dark")
# Update all cards
for card in self.cards:
card.update_theme(self.current_theme)
# Update header
self.title_label.configure(text_color=colors["text"])
self.subtitle_label.configure(text_color=colors["text_muted"])
self.time_label.configure(text_color=colors["text_secondary"])
# Update status bar
self.status_frame.configure(fg_color=colors["card"])
self.status_label.configure(text_color=colors["text_secondary"])
self.update_label.configure(text_color=colors["text_muted"])
# Update text boxes
self.log_text.configure(fg_color=colors["card_inner"], text_color=colors["green"])
self.positions_text.configure(fg_color=colors["card_inner"], text_color=colors["text"])
# Update settings labels
if hasattr(self, 'settings_labels'):
for lbl, val_lbl in self.settings_labels:
lbl.configure(text_color=colors["text_secondary"])
val_lbl.configure(text_color=colors["text"])
# Update golden time frame
is_golden = hasattr(self, 'golden_frame')
if is_golden:
# Reapply golden time based on current state
current_text = self.golden_label.cget("text")
if "YES" in current_text:
self.golden_frame.configure(fg_color=colors["highlight"])
self.golden_label.configure(text_color=colors["green"])
else:
self.golden_frame.configure(fg_color=colors["highlight_off"])
self.golden_label.configure(text_color=colors["text_secondary"])
def create_header(self):
"""Create header section"""
colors = THEMES[self.current_theme]
header_frame = ctk.CTkFrame(self, fg_color="transparent", height=50)
header_frame.pack(fill="x", padx=15, pady=(10, 5))
header_frame.pack_propagate(False)
# Logo/Title
self.title_label = ctk.CTkLabel(
header_frame,
text="AI TRADING BOT",
font=ctk.CTkFont(size=20, weight="bold"),
text_color=colors["text"]
)
self.title_label.pack(side="left")
self.subtitle_label = ctk.CTkLabel(
header_frame,
text=" Live",
font=ctk.CTkFont(size=12),
text_color=colors["text_muted"]
)
self.subtitle_label.pack(side="left", pady=(4, 0))
# Theme toggle button
self.theme_btn = ctk.CTkButton(
header_frame,
text="☀️ Light",
width=80,
height=28,
corner_radius=14,
font=ctk.CTkFont(size=11),
command=self.toggle_theme
)
self.theme_btn.pack(side="right", padx=5)
# Connection status
self.connection_label = ctk.CTkLabel(
header_frame,
text="● Connecting...",
font=ctk.CTkFont(size=12),
text_color=colors["orange"]
)
self.connection_label.pack(side="right", padx=10)
# Time display
self.time_label = ctk.CTkLabel(
header_frame,
text="--:--:-- WIB",
font=ctk.CTkFont(size=12, weight="bold"),
text_color=colors["text_secondary"]
)
self.time_label.pack(side="right", padx=10)
def create_main_layout(self):
"""Create main dashboard layout - responsive 2-column for split screen"""
# Scrollable main container for small windows
self.main_scroll = ctk.CTkScrollableFrame(self, fg_color="transparent")
self.main_scroll.pack(fill="both", expand=True, padx=10, pady=5)
# Configure 2-column grid (responsive for split screen)
self.main_scroll.grid_columnconfigure(0, weight=1, minsize=280)
self.main_scroll.grid_columnconfigure(1, weight=1, minsize=280)
# Row 0: Price & Account
self.create_price_card(self.main_scroll, 0, 0)
self.create_account_card(self.main_scroll, 0, 1)
# Row 1: Session & Risk
self.create_session_card(self.main_scroll, 1, 0)
self.create_risk_card(self.main_scroll, 1, 1)
# Row 2: SMC & ML
self.create_smc_card(self.main_scroll, 2, 0)
self.create_ml_card(self.main_scroll, 2, 1)
# Row 3: Regime & Positions
self.create_regime_card(self.main_scroll, 3, 0)
self.create_positions_card(self.main_scroll, 3, 1)
# Row 4: Settings (full width)
self.create_settings_card(self.main_scroll, 4, 0)
# Row 5: Log (full width)
self.create_log_card(self.main_scroll, 5, 0)
def create_price_card(self, parent, row, col):
"""Price information card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "PRICE", theme=self.current_theme)
card.grid(row=row, column=col, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
# Price value
self.price_label = ctk.CTkLabel(
card.content,
text="-----.--",
font=ctk.CTkFont(size=28, weight="bold"),
text_color=colors["green"]
)
self.price_label.pack(pady=(5, 0))
# Symbol
self.symbol_label = ctk.CTkLabel(
card.content,
text="XAUUSD",
font=ctk.CTkFont(size=11),
text_color=colors["text_muted"]
)
self.symbol_label.pack()
# Spread
self.spread_label = ctk.CTkLabel(
card.content,
text="Spread: -- pips",
font=ctk.CTkFont(size=10),
text_color=colors["text_secondary"]
)
self.spread_label.pack(pady=(3, 0))
def create_account_card(self, parent, row, col):
"""Account information card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "ACCOUNT", theme=self.current_theme)
card.grid(row=row, column=col, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
# Balance
balance_frame = ctk.CTkFrame(card.content, fg_color="transparent")
balance_frame.pack(fill="x", pady=2)
ctk.CTkLabel(balance_frame, text="Balance:", font=ctk.CTkFont(size=11), text_color=colors["text_secondary"]).pack(side="left")
self.balance_label = ctk.CTkLabel(balance_frame, text="$-----.--", font=ctk.CTkFont(size=11, weight="bold"), text_color=colors["text"])
self.balance_label.pack(side="right")
# Equity
equity_frame = ctk.CTkFrame(card.content, fg_color="transparent")
equity_frame.pack(fill="x", pady=2)
ctk.CTkLabel(equity_frame, text="Equity:", font=ctk.CTkFont(size=11), text_color=colors["text_secondary"]).pack(side="left")
self.equity_label = ctk.CTkLabel(equity_frame, text="$-----.--", font=ctk.CTkFont(size=11, weight="bold"), text_color=colors["text"])
self.equity_label.pack(side="right")
# P/L
pl_frame = ctk.CTkFrame(card.content, fg_color="transparent")
pl_frame.pack(fill="x", pady=2)
ctk.CTkLabel(pl_frame, text="P/L:", font=ctk.CTkFont(size=11), text_color=colors["text_secondary"]).pack(side="left")
self.pl_label = ctk.CTkLabel(pl_frame, text="$0.00", font=ctk.CTkFont(size=12, weight="bold"), text_color=colors["green"])
self.pl_label.pack(side="right")
def create_session_card(self, parent, row, col):
"""Session information card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "SESSION", theme=self.current_theme)
card.grid(row=row, column=col, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
# Current session
self.session_label = ctk.CTkLabel(
card.content,
text="Loading...",
font=ctk.CTkFont(size=12, weight="bold"),
text_color=colors["orange"]
)
self.session_label.pack(pady=(3, 0))
# Golden time indicator
self.golden_frame = ctk.CTkFrame(card.content, fg_color=colors["highlight_off"], corner_radius=6)
self.golden_frame.pack(pady=6, padx=3, fill="x")
self.golden_label = ctk.CTkLabel(
self.golden_frame,
text="GOLDEN: --",
font=ctk.CTkFont(size=10, weight="bold"),
text_color=colors["text_secondary"]
)
self.golden_label.pack(pady=4)
# Can trade
self.can_trade_label = ctk.CTkLabel(
card.content,
text="Can Trade: --",
font=ctk.CTkFont(size=10),
text_color=colors["text_secondary"]
)
self.can_trade_label.pack()
def create_risk_card(self, parent, row, col):
"""Risk status card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "RISK STATUS", theme=self.current_theme)
card.grid(row=row, column=col, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
# Daily loss
dl_frame = ctk.CTkFrame(card.content, fg_color="transparent")
dl_frame.pack(fill="x", pady=2)
ctk.CTkLabel(dl_frame, text="Daily Loss:", font=ctk.CTkFont(size=10), text_color=colors["text_secondary"]).pack(side="left")
self.daily_loss_label = ctk.CTkLabel(dl_frame, text="$0.00", font=ctk.CTkFont(size=10, weight="bold"), text_color=colors["red"])
self.daily_loss_label.pack(side="right")
# Daily profit
dp_frame = ctk.CTkFrame(card.content, fg_color="transparent")
dp_frame.pack(fill="x", pady=2)
ctk.CTkLabel(dp_frame, text="Daily Profit:", font=ctk.CTkFont(size=10), text_color=colors["text_secondary"]).pack(side="left")
self.daily_profit_label = ctk.CTkLabel(dp_frame, text="$0.00", font=ctk.CTkFont(size=10, weight="bold"), text_color=colors["green"])
self.daily_profit_label.pack(side="right")
# Consecutive losses
cl_frame = ctk.CTkFrame(card.content, fg_color="transparent")
cl_frame.pack(fill="x", pady=2)
ctk.CTkLabel(cl_frame, text="Consec. Losses:", font=ctk.CTkFont(size=10), text_color=colors["text_secondary"]).pack(side="left")
self.consec_loss_label = ctk.CTkLabel(cl_frame, text="0", font=ctk.CTkFont(size=10, weight="bold"), text_color=colors["text"])
self.consec_loss_label.pack(side="right")
def create_smc_card(self, parent, row, col):
"""SMC Signal card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "SMC SIGNAL", theme=self.current_theme)
card.grid(row=row, column=col, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
# Signal
self.smc_signal_label = ctk.CTkLabel(
card.content,
text="---",
font=ctk.CTkFont(size=24, weight="bold"),
text_color=colors["text_muted"]
)
self.smc_signal_label.pack(pady=(5, 3))
# Confidence bar
self.smc_confidence_bar = ctk.CTkProgressBar(card.content, height=6, corner_radius=3)
self.smc_confidence_bar.pack(pady=4, fill="x", padx=10)
self.smc_confidence_bar.set(0)
# Confidence text
self.smc_conf_label = ctk.CTkLabel(
card.content,
text="Confidence: --%",
font=ctk.CTkFont(size=10),
text_color=colors["text_secondary"]
)
self.smc_conf_label.pack()
# Reason
self.smc_reason_label = ctk.CTkLabel(
card.content,
text="Waiting...",
font=ctk.CTkFont(size=9),
text_color=colors["text_muted"],
wraplength=250
)
self.smc_reason_label.pack(pady=(3, 0))
def create_ml_card(self, parent, row, col):
"""ML Prediction card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "ML PREDICTION", theme=self.current_theme)
card.grid(row=row, column=col, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
# Signal
self.ml_signal_label = ctk.CTkLabel(
card.content,
text="---",
font=ctk.CTkFont(size=24, weight="bold"),
text_color=colors["text_muted"]
)
self.ml_signal_label.pack(pady=(5, 3))
# Confidence bar
self.ml_confidence_bar = ctk.CTkProgressBar(card.content, height=6, corner_radius=3)
self.ml_confidence_bar.pack(pady=4, fill="x", padx=10)
self.ml_confidence_bar.set(0)
# Confidence text
self.ml_conf_label = ctk.CTkLabel(
card.content,
text="Confidence: --%",
font=ctk.CTkFont(size=10),
text_color=colors["text_secondary"]
)
self.ml_conf_label.pack()
# Probabilities
self.ml_prob_label = ctk.CTkLabel(
card.content,
text="Buy: --% | Sell: --%",
font=ctk.CTkFont(size=9),
text_color=colors["text_muted"]
)
self.ml_prob_label.pack(pady=(3, 0))
def create_regime_card(self, parent, row, col):
"""Market Regime card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "MARKET REGIME", theme=self.current_theme)
card.grid(row=row, column=col, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
# Regime
self.regime_label = ctk.CTkLabel(
card.content,
text="---",
font=ctk.CTkFont(size=16, weight="bold"),
text_color=colors["text_muted"]
)
self.regime_label.pack(pady=(8, 5))
# Volatility
vol_frame = ctk.CTkFrame(card.content, fg_color="transparent")
vol_frame.pack(fill="x", pady=2)
ctk.CTkLabel(vol_frame, text="Volatility:", font=ctk.CTkFont(size=10), text_color=colors["text_secondary"]).pack(side="left")
self.volatility_label = ctk.CTkLabel(vol_frame, text="--", font=ctk.CTkFont(size=10, weight="bold"), text_color=colors["text"])
self.volatility_label.pack(side="right")
# Confidence
conf_frame = ctk.CTkFrame(card.content, fg_color="transparent")
conf_frame.pack(fill="x", pady=2)
ctk.CTkLabel(conf_frame, text="Confidence:", font=ctk.CTkFont(size=10), text_color=colors["text_secondary"]).pack(side="left")
self.regime_conf_label = ctk.CTkLabel(conf_frame, text="--%", font=ctk.CTkFont(size=10, weight="bold"), text_color=colors["text"])
self.regime_conf_label.pack(side="right")
def create_positions_card(self, parent, row, col):
"""Open Positions card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "OPEN POSITIONS", theme=self.current_theme)
card.grid(row=row, column=col, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
# Positions text
self.positions_text = ctk.CTkTextbox(
card.content,
font=ctk.CTkFont(family="Consolas", size=10),
fg_color=colors["card_inner"],
text_color=colors["text"],
height=90,
corner_radius=6
)
self.positions_text.pack(fill="both", expand=True)
self.positions_text.insert("1.0", "No open positions")
self.positions_text.configure(state="disabled")
def create_settings_card(self, parent, row, col):
"""Settings display card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "SETTINGS", theme=self.current_theme)
card.grid(row=row, column=col, columnspan=2, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
# Settings grid - use 2 columns for compact layout
settings_frame = ctk.CTkFrame(card.content, fg_color="transparent")
settings_frame.pack(fill="both", expand=True)
settings_frame.grid_columnconfigure(0, weight=1)
settings_frame.grid_columnconfigure(1, weight=1)
settings = [
("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"),
]
self.settings_labels = [] # Store for theme updates
for i, (label, value) in enumerate(settings):
row_idx = i // 2
col_idx = i % 2
row_frame = ctk.CTkFrame(settings_frame, fg_color="transparent")
row_frame.grid(row=row_idx, column=col_idx, sticky="w", padx=5, pady=1)
lbl = ctk.CTkLabel(
row_frame,
text=f"{label}:",
font=ctk.CTkFont(size=10),
text_color=colors["text_secondary"],
anchor="w"
)
lbl.pack(side="left")
val_lbl = ctk.CTkLabel(
row_frame,
text=f" {value}",
font=ctk.CTkFont(size=10, weight="bold"),
text_color=colors["text"],
anchor="w"
)
val_lbl.pack(side="left")
self.settings_labels.append((lbl, val_lbl))
def create_log_card(self, parent, row, col):
"""Activity log card"""
colors = THEMES[self.current_theme]
card = ModernCard(parent, "ACTIVITY LOG", theme=self.current_theme)
card.grid(row=row, column=col, columnspan=2, padx=4, pady=4, sticky="nsew")
self.cards.append(card)
self.log_text = ctk.CTkTextbox(
card.content,
font=ctk.CTkFont(family="Consolas", size=10),
fg_color=colors["card_inner"],
text_color=colors["green"],
height=100,
corner_radius=6
)
self.log_text.pack(fill="both", expand=True)
def create_status_bar(self):
"""Create status bar"""
colors = THEMES[self.current_theme]
self.status_frame = ctk.CTkFrame(self, fg_color=colors["card"], height=28, corner_radius=0)
self.status_frame.pack(fill="x", side="bottom")
self.status_frame.pack_propagate(False)
self.status_label = ctk.CTkLabel(
self.status_frame,
text="Initializing...",
font=ctk.CTkFont(size=10),
text_color=colors["text_secondary"]
)
self.status_label.pack(side="left", padx=12, pady=4)
self.update_label = ctk.CTkLabel(
self.status_frame,
text="Last update: --:--:--",
font=ctk.CTkFont(size=10),
text_color=colors["text_muted"]
)
self.update_label.pack(side="right", padx=12, pady=4)
def log(self, message):
"""Add message to activity log"""
timestamp = datetime.now().strftime("%H:%M:%S")
self.log_text.insert("end", f"[{timestamp}] {message}\n")
self.log_text.see("end")
def connect_systems(self):
"""Connect to MT5 and initialize components"""
colors = THEMES[self.current_theme]
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!")
self.connection_label.configure(text="● Disconnected", text_color=colors["red"])
return
self.log("MT5 connected successfully!")
self.connection_label.configure(text="● Connected", text_color=colors["green"])
# Initialize components
self.smc = SMCAnalyzer()
self.ml = TradingModel(model_path="models/xgboost_model")
self.ml.load()
self.hmm = MarketRegimeDetector(model_path="models/hmm_regime")
self.hmm.load()
self.session = SessionFilter()
self.feature_eng = FeatureEngineer()
self.log("All components initialized")
if self.ml.fitted:
self.log(f"ML Model loaded ({len(self.ml.feature_names)} features)")
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}")
self.connection_label.configure(text="● Error", text_color=colors["red"])
def update_loop(self):
"""Main update loop"""
while self.running:
try:
self.after(0, self.update_data)
time.sleep(1)
except Exception as e:
self.after(0, lambda: self.log(f"Update error: {e}"))
time.sleep(5)
def update_data(self):
"""Fetch and update all data"""
if not self.mt5:
return
# Update time
wib = ZoneInfo("Asia/Jakarta")
now = datetime.now(wib)
self.time_label.configure(text=now.strftime("%H:%M:%S WIB"))
# Check golden time (use theme colors)
colors = THEMES[self.current_theme]
is_golden = 19 <= now.hour < 23
if is_golden:
self.golden_frame.configure(fg_color=colors["highlight"])
self.golden_label.configure(text="GOLDEN TIME: YES", text_color=colors["green"])
else:
self.golden_frame.configure(fg_color=colors["highlight_off"])
self.golden_label.configure(text="GOLDEN TIME: NO", text_color=colors["text_secondary"])
# Update price
try:
tick = self.mt5.get_tick(self.config.symbol)
if tick:
price = (tick.bid + tick.ask) / 2
spread = (tick.ask - tick.bid) * 100
self.price_label.configure(text=f"{price:.2f}")
self.spread_label.configure(text=f"Spread: {spread:.1f} pips")
except:
pass
# Update account
try:
balance = self.mt5.account_balance
equity = self.mt5.account_equity
profit = equity - balance
self.balance_label.configure(text=f"${balance:,.2f}")
self.equity_label.configure(text=f"${equity:,.2f}")
if profit >= 0:
self.pl_label.configure(text=f"+${profit:.2f}", text_color=colors["green"])
else:
self.pl_label.configure(text=f"-${abs(profit):.2f}", text_color=colors["red"])
except:
pass
# Update session
try:
session_info = self.session.get_status_report()
if session_info:
self.session_label.configure(text=session_info.get('current_session', 'Unknown'))
can_trade, reason, _ = self.session.can_trade()
if can_trade:
self.can_trade_label.configure(text="Can Trade: YES", text_color=colors["green"])
else:
self.can_trade_label.configure(text="Can Trade: NO", text_color=colors["red"])
except:
pass
# Get market data and update signals
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:
pass
# Update 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.update_label.configure(text=f"Last update: {self.last_update.strftime('%H:%M:%S')}")
self.status_label.configure(text="Running...")
def update_signals(self, df):
"""Update SMC, ML, and Regime signals"""
colors = THEMES[self.current_theme]
# Build complete features
df = self.feature_eng.calculate_all(df, include_ml_features=True)
df = self.smc.calculate_all(df)
# Regime
try:
df = self.hmm.predict(df)
regime = self.hmm.get_current_state(df)
if regime:
regime_name = regime.regime.value.replace('_', ' ').title()
self.regime_label.configure(text=regime_name)
self.volatility_label.configure(text=f"{regime.volatility:.2f}")
self.regime_conf_label.configure(text=f"{regime.confidence:.0%}")
if "HIGH" in regime.regime.value:
self.regime_label.configure(text_color=colors["red"])
elif "LOW" in regime.regime.value:
self.regime_label.configure(text_color=colors["green"])
else:
self.regime_label.configure(text_color=colors["orange"])
except:
pass
# SMC Signal
try:
smc_signal = self.smc.generate_signal(df)
if smc_signal:
self.smc_signal_label.configure(text=smc_signal.signal_type)
self.smc_confidence_bar.set(smc_signal.confidence)
self.smc_conf_label.configure(text=f"Confidence: {smc_signal.confidence:.0%}")
self.smc_reason_label.configure(text=smc_signal.reason[:60] + "..." if len(smc_signal.reason) > 60 else smc_signal.reason)
if smc_signal.signal_type == "BUY":
self.smc_signal_label.configure(text_color=colors["green"])
self.smc_confidence_bar.configure(progress_color=colors["green"])
else:
self.smc_signal_label.configure(text_color=colors["red"])
self.smc_confidence_bar.configure(progress_color=colors["red"])
else:
self.smc_signal_label.configure(text="NO SIGNAL", text_color=colors["text_muted"])
self.smc_confidence_bar.set(0)
self.smc_conf_label.configure(text="Confidence: --%")
self.smc_reason_label.configure(text="Waiting for setup...")
except:
pass
# ML Prediction
try:
if self.ml.fitted:
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:
self.ml_signal_label.configure(text=ml_pred.signal)
self.ml_confidence_bar.set(ml_pred.confidence)
self.ml_conf_label.configure(text=f"Confidence: {ml_pred.confidence:.0%}")
buy_prob = ml_pred.probability
sell_prob = 1.0 - buy_prob
self.ml_prob_label.configure(text=f"Buy: {buy_prob:.0%} | Sell: {sell_prob:.0%}")
if ml_pred.signal == "BUY":
self.ml_signal_label.configure(text_color=colors["green"])
self.ml_confidence_bar.configure(progress_color=colors["green"])
elif ml_pred.signal == "SELL":
self.ml_signal_label.configure(text_color=colors["red"])
self.ml_confidence_bar.configure(progress_color=colors["red"])
else:
self.ml_signal_label.configure(text_color=colors["orange"])
self.ml_confidence_bar.configure(progress_color=colors["orange"])
except:
pass
def update_positions(self, positions):
"""Update positions display"""
self.positions_text.configure(state="normal")
self.positions_text.delete("1.0", "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("end", line)
self.positions_text.configure(state="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_label.configure(text=f"${float(value):,.2f}")
elif key == 'daily_profit':
self.daily_profit_label.configure(text=f"${float(value):,.2f}")
elif key == 'consecutive_losses':
self.consec_loss_label.configure(text=value)
except:
pass
def on_closing(self):
"""Handle window close"""
self.running = False
if self.mt5:
self.mt5.disconnect()
self.destroy()
def main():
app = TradingDashboard()
app.protocol("WM_DELETE_WINDOW", app.on_closing)
app.mainloop()
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
@echo off
echo ========================================
echo AI Trading Bot - Web Dashboard
echo ========================================
echo.
:: Start API Server
echo Starting API Server on port 8000...
start "API Server" cmd /k "cd /d %~dp0 && python web-dashboard\api\main.py"
:: Wait a bit for API to start
timeout /t 3 /nobreak > nul
:: Start Next.js Frontend
echo Starting Web Dashboard on port 3000...
start "Web Dashboard" cmd /k "cd /d %~dp0\web-dashboard && npm run dev"
echo.
echo ========================================
echo Dashboard starting...
echo.
echo API Server: http://localhost:8000
echo Web Dashboard: http://localhost:3000
echo ========================================
echo.
echo Press any key to open dashboard in browser...
pause > nul
start http://localhost:3000