"""
EURUSD 鱼身突破策略 - 自动交易系统 v1.5
修复分割器和定时器跨线程问题
修复下单填充模式错误，取消人工确认
"""

import sys
import os
from datetime import datetime, timedelta
import time
import threading
from collections import deque

from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QTableWidget, QTableWidgetItem, QTextEdit, QLabel, QPushButton,
    QCheckBox, QGroupBox, QHeaderView, QSplitter,
    QMessageBox, QProgressBar
)
from PyQt5.QtCore import (
    Qt, QTimer, QThread, pyqtSignal, QObject
)
from PyQt5.QtGui import QFont, QColor, QTextCursor, QBrush

import MetaTrader5 as mt5
import pandas as pd
import numpy as np
import pytz

# ==================== 策略配置 ====================
class StrategyConfig:
    # 时间配置（所有时间均为UTC0）
    ASIAN_START_HOUR_UTC0 = 21
    ASIAN_END_HOUR_UTC0 = 6
    TRADE_START_HOUR_UTC0 = 8
    TRADE_END_HOUR_UTC0 = 22
    FORCE_CLOSE_HOUR_UTC0 = 23
    
    # 策略参数
    EMA_PERIOD = 60
    RSI_PERIOD = 14
    ATR_PERIOD = 14
    RSI_OVERBOUGHT = 65.0
    RSI_OVERSOLD = 35.0
    EMA_SLOPE_PERIOD = 5
    MIN_ASIAN_RANGE_PIPS = 12
    VOLATILITY_THRESHOLD = 15
    
    # 止盈止损
    TP_ATR_MULTIPLIER = 1.5
    SL_ATR_MULTIPLIER = 1.0
    
    # 成本
    SLIPPAGE_POINTS = 5
    SPREAD_POINTS = 1.5
    
    # 交易手数
    FIXED_LOT_SIZE = 1.0
    
    # 时间框架（分钟）
    TIMEFRAME_MINUTES = 15

# ==================== 货币对配置（6个）====================
CURRENCY_PAIRS = {
    'EURUSD': {
        'pip_value': 10, 'point': 0.00001, 'pip_multiplier': 10,
        'digits': 5, 'min_asian_range': 12, 'volatility_threshold': 15,
        'enabled': True
    },
    'GBPUSD': {
        'pip_value': 10, 'point': 0.00001, 'pip_multiplier': 10,
        'digits': 5, 'min_asian_range': 15, 'volatility_threshold': 18,
        'enabled': True
    },
    'AUDUSD': {
        'pip_value': 10, 'point': 0.00001, 'pip_multiplier': 10,
        'digits': 5, 'min_asian_range': 10, 'volatility_threshold': 12,
        'enabled': True
    },
    'USDCAD': {
        'pip_value': 7.5, 'point': 0.00001, 'pip_multiplier': 10,
        'digits': 5, 'min_asian_range': 12, 'volatility_threshold': 15,
        'enabled': True
    },
    'USDCHF': {
        'pip_value': 11, 'point': 0.00001, 'pip_multiplier': 10,
        'digits': 5, 'min_asian_range': 12, 'volatility_threshold': 14,
        'enabled': True
    },
    'NZDUSD': {
        'pip_value': 10, 'point': 0.00001, 'pip_multiplier': 10,
        'digits': 5, 'min_asian_range': 10, 'volatility_threshold': 12,
        'enabled': True
    }
}

# 时区定义
UTC0_TZ = pytz.timezone("Etc/UTC")
BEIJING_TZ = pytz.timezone("Asia/Shanghai")


class TimeZoneConverter:
    """时区转换工具"""
    
    @staticmethod
    def get_current_utc0_time():
        return datetime.now(UTC0_TZ)
    
    @staticmethod
    def get_current_beijing_time():
        return datetime.now(BEIJING_TZ)
    
    @staticmethod
    def format_time_info():
        utc0_now = TimeZoneConverter.get_current_utc0_time()
        bj_now = TimeZoneConverter.get_current_beijing_time()
        
        return {
            'utc0_time': utc0_now,
            'beijing_time': bj_now,
            'utc0_hour': utc0_now.hour,
            'beijing_hour': bj_now.hour,
            'utc0_str': utc0_now.strftime("%H:%M:%S"),
            'beijing_str': bj_now.strftime("%H:%M:%S")
        }
    
    @staticmethod
    def get_next_bar_time(timeframe_minutes=15):
        now = TimeZoneConverter.get_current_utc0_time()
        
        current_minute = now.minute
        current_bar_end_minute = ((current_minute // timeframe_minutes) + 1) * timeframe_minutes
        
        if current_bar_end_minute >= 60:
            next_bar_time = now.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
        else:
            next_bar_time = now.replace(minute=current_bar_end_minute, second=0, microsecond=0)
        
        seconds_until_next = (next_bar_time - now).total_seconds()
        
        if seconds_until_next <= 0:
            current_minute = next_bar_time.minute
            current_bar_end_minute = ((current_minute // timeframe_minutes) + 1) * timeframe_minutes
            if current_bar_end_minute >= 60:
                next_bar_time = next_bar_time.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
            else:
                next_bar_time = next_bar_time.replace(minute=current_bar_end_minute, second=0, microsecond=0)
            seconds_until_next = (next_bar_time - now).total_seconds()
        
        return next_bar_time, int(seconds_until_next)


class StrategyAnalyzer:
    """策略分析器"""
    
    def __init__(self, symbol, config):
        self.symbol = symbol
        self.symbol_config = config
        self.last_analyzed_bar_time = None
        
    def calculate_ema_slope(self, ema_series, period=5):
        if len(ema_series) < period + 1:
            return 0
        return (ema_series.iloc[-1] - ema_series.iloc[-period-1]) / period
    
    def calculate_atr(self, df, period=14):
        high = df['high']
        low = df['low']
        close = df['close'].shift(1)
        
        tr1 = high - low
        tr2 = abs(high - close)
        tr3 = abs(low - close)
        
        tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
        atr = tr.rolling(window=period).mean()
        
        return atr
    
    def get_asian_range(self, df, current_time):
        current_date = current_time.date()
        
        asian_start = datetime.combine(current_date, datetime.min.time()) - timedelta(days=1) + timedelta(hours=StrategyConfig.ASIAN_START_HOUR_UTC0)
        asian_end = datetime.combine(current_date, datetime.min.time()) + timedelta(hours=StrategyConfig.ASIAN_END_HOUR_UTC0)
        
        asian_start = UTC0_TZ.localize(asian_start)
        asian_end = UTC0_TZ.localize(asian_end)
        
        asian_data = df[(df['time'] >= asian_start) & (df['time'] <= asian_end)]
        
        if len(asian_data) == 0:
            return None, None, 0
        
        asian_high = asian_data['high'].max()
        asian_low = asian_data['low'].min()
        asian_range_pips = (asian_high - asian_low) / self.symbol_config['point'] / self.symbol_config['pip_multiplier']
        
        return asian_high, asian_low, asian_range_pips
    
    def check_breakout_quality(self, df, direction, asian_level):
        if len(df) < 5:
            return False
        
        current_row = df.iloc[-1]
        body_size = abs(current_row['close'] - current_row['open'])
        avg_body = df.iloc[-6:-1]['close'].diff().abs().mean()
        
        if body_size < avg_body * 1.2:
            return False
        
        return True
    
    def analyze(self, df, current_time):
        if len(df) < 100:
            return None
        
        current_bar_time = df.iloc[-1]['time']
        if self.last_analyzed_bar_time == current_bar_time:
            return None
        
        self.last_analyzed_bar_time = current_bar_time
        
        df['ema60'] = df['close'].ewm(span=StrategyConfig.EMA_PERIOD, adjust=False).mean()
        df['ema_slope'] = self.calculate_ema_slope(df['ema60'], StrategyConfig.EMA_SLOPE_PERIOD)
        df['atr'] = self.calculate_atr(df, StrategyConfig.ATR_PERIOD)
        
        delta = df['close'].diff()
        gain = delta.clip(lower=0).rolling(window=StrategyConfig.RSI_PERIOD).mean()
        loss = (-delta.clip(upper=0)).rolling(window=StrategyConfig.RSI_PERIOD).mean()
        rs = gain / loss
        df['rsi'] = 100 - (100 / (1 + rs))
        
        current_row = df.iloc[-1]
        close_price = current_row['close']
        ema60 = df.iloc[-2]['ema60'] if len(df) > 1 else close_price
        rsi = df.iloc[-2]['rsi'] if len(df) > 1 else 50
        current_atr = df.iloc[-2]['atr'] if len(df) > 1 else 0
        
        if pd.isna(ema60) or pd.isna(rsi) or pd.isna(current_atr):
            return None
        
        asian_high, asian_low, asian_range_pips = self.get_asian_range(df, current_time)
        
        if asian_high is None or asian_range_pips < self.symbol_config['min_asian_range']:
            return None
        
        atr_pips = current_atr / self.symbol_config['point'] / self.symbol_config['pip_multiplier']
        
        # 多头信号
        if (close_price > asian_high and 
            close_price > ema60 and 
            rsi < StrategyConfig.RSI_OVERBOUGHT and
            rsi > 30):
            
            if not self.check_breakout_quality(df, 'buy', asian_high):
                return None
            
            tp_distance = current_atr * StrategyConfig.TP_ATR_MULTIPLIER
            sl_distance = current_atr * StrategyConfig.SL_ATR_MULTIPLIER
            
            entry_price = close_price + (StrategyConfig.SPREAD_POINTS * self.symbol_config['point'])
            
            return {
                'symbol': self.symbol,
                'direction': 'buy',
                'entry_price': entry_price,
                'sl_price': entry_price - sl_distance,
                'tp_price': entry_price + tp_distance,
                'asian_range_pips': asian_range_pips,
                'atr_pips': atr_pips,
                'rsi': rsi,
                'time': current_time,
                'bar_time': current_bar_time
            }
        
        # 空头信号
        elif (close_price < asian_low and 
              close_price < ema60 and 
              rsi > StrategyConfig.RSI_OVERSOLD and
              rsi < 70):
            
            if not self.check_breakout_quality(df, 'sell', asian_low):
                return None
            
            tp_distance = current_atr * StrategyConfig.TP_ATR_MULTIPLIER
            sl_distance = current_atr * StrategyConfig.SL_ATR_MULTIPLIER
            
            entry_price = close_price - (StrategyConfig.SPREAD_POINTS * self.symbol_config['point'])
            
            return {
                'symbol': self.symbol,
                'direction': 'sell',
                'entry_price': entry_price,
                'sl_price': entry_price + sl_distance,
                'tp_price': entry_price - tp_distance,
                'asian_range_pips': asian_range_pips,
                'atr_pips': atr_pips,
                'rsi': rsi,
                'time': current_time,
                'bar_time': current_bar_time
            }
        
        return None


class MTTrader:
    """MT5交易执行器"""
    
    def __init__(self):
        self.connected = False
        
    def connect(self):
        if not mt5.initialize():
            return False, "MT5初始化失败"
        
        self.connected = True
        account_info = mt5.account_info()
        if account_info:
            return True, f"连接成功，账户: {account_info.login}"
        return True, "连接成功"
    
    def disconnect(self):
        mt5.shutdown()
        self.connected = False
    
    def get_positions(self):
        positions = mt5.positions_get()
        if positions is None:
            return []
        
        pos_list = []
        for pos in positions:
            pos_list.append({
                'ticket': pos.ticket,
                'symbol': pos.symbol,
                'type': 'buy' if pos.type == mt5.ORDER_TYPE_BUY else 'sell',
                'volume': pos.volume,
                'open_price': pos.price_open,
                'current_price': pos.price_current,
                'sl': pos.sl,
                'tp': pos.tp,
                'profit': pos.profit,
                'time': datetime.fromtimestamp(pos.time, UTC0_TZ)
            })
        
        return pos_list
    
    def has_position(self, symbol, direction):
        positions = self.get_positions()
        for pos in positions:
            if pos['symbol'] == symbol and pos['type'] == direction:
                return True
        return False
    
    def place_order(self, signal):
        """下单 - 修复填充模式错误"""
        symbol = signal['symbol']
        direction = signal['direction']
        entry_price = signal['entry_price']
        sl_price = signal['sl_price']
        tp_price = signal['tp_price']
        
        # 获取货币对信息
        symbol_info = mt5.symbol_info(symbol)
        if symbol_info is None:
            return False, f"无法获取{symbol}信息"
        
        # 尝试多种填充模式
        filling_modes = [
            mt5.ORDER_FILLING_FOK,      # Fill or Kill - 推荐
            mt5.ORDER_FILLING_RETURN,   # Return
            mt5.ORDER_FILLING_IOC,      # Immediate or Cancel
        ]
        
        request_base = {
            "action": mt5.TRADE_ACTION_DEAL,
            "symbol": symbol,
            "volume": StrategyConfig.FIXED_LOT_SIZE,
            "type": mt5.ORDER_TYPE_BUY if direction == 'buy' else mt5.ORDER_TYPE_SELL,
            "price": entry_price,
            "sl": sl_price,
            "tp": tp_price,
            "deviation": 20,
            "magic": 234000,
            "comment": "FishBody Breakout M15",
            "type_time": mt5.ORDER_TIME_GTC,
        }
        
        last_error = ""
        for filling_mode in filling_modes:
            request = request_base.copy()
            request["type_filling"] = filling_mode
            result = mt5.order_send(request)
            
            if result.retcode == mt5.TRADE_RETCODE_DONE:
                return True, f"下单成功，订单号: {result.order}"
            elif result.retcode == mt5.TRADE_RETCODE_DONE_PARTIAL:
                return True, f"部分成交，订单号: {result.order}"
            else:
                last_error = f"{result.comment} (retcode={result.retcode})"
        
        return False, f"下单失败: {last_error}"
    
    def get_historical_data(self, symbol, timeframe, count=500):
        rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, count)
        if rates is None or len(rates) == 0:
            return None
        
        df = pd.DataFrame(rates)
        df['time'] = pd.to_datetime(df['time'], unit='s')
        df['time'] = df['time'].dt.tz_localize('UTC')
        
        return df
    
    def get_latest_bar_time(self, symbol, timeframe):
        rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, 1)
        if rates is None or len(rates) == 0:
            return None
        
        return datetime.fromtimestamp(rates[0]['time'], UTC0_TZ)


class AnalysisWorker(QObject):
    """分析工作器 - 修复定时器跨线程问题"""
    
    log_signal = pyqtSignal(str, str)
    signal_detected = pyqtSignal(dict)
    positions_update = pyqtSignal(list)
    countdown_update = pyqtSignal(str)
    status_update = pyqtSignal(str)
    
    def __init__(self, enabled_symbols):
        super().__init__()
        self.enabled_symbols = enabled_symbols
        self.running = False
        self.trader = MTTrader()
        self.analyzers = {}
        self.last_bar_times = {}
        self.is_analyzing = False
        
        # 初始化分析器
        for symbol in self.enabled_symbols:
            if symbol in CURRENCY_PAIRS:
                self.analyzers[symbol] = StrategyAnalyzer(symbol, CURRENCY_PAIRS[symbol])
                self.last_bar_times[symbol] = None
        
        self.next_bar_time = None
        self.seconds_until_next = 0
        
        # 定时器将在主线程中创建和使用
        self.countdown_timer = None
        self.analysis_timer = None
    
    def init_timers(self):
        """在主线程中初始化定时器"""
        self.countdown_timer = QTimer()
        self.countdown_timer.timeout.connect(self.update_countdown)
        
        self.analysis_timer = QTimer()
        self.analysis_timer.timeout.connect(self.perform_analysis)
        self.analysis_timer.setSingleShot(True)
    
    def start(self):
        success, msg = self.trader.connect()
        if not success:
            self.log_signal.emit(f"MT5连接失败: {msg}", "error")
            return False
        
        self.log_signal.emit(msg, "success")
        self.log_signal.emit(f"分析模式: 每个M15 K线结束时分析一次", "info")
        
        self.running = True
        
        # 初始化定时器
        self.init_timers()
        
        # 启动倒计时
        self.countdown_timer.start(1000)
        
        # 调度第一次分析
        self.schedule_next_analysis()
        
        return True
    
    def stop(self):
        self.running = False
        
        if self.countdown_timer:
            self.countdown_timer.stop()
        if self.analysis_timer:
            self.analysis_timer.stop()
            
        self.trader.disconnect()
        self.log_signal.emit("分析工作器已停止", "info")
    
    def update_countdown(self):
        if not self.running:
            return
        
        self.next_bar_time, self.seconds_until_next = TimeZoneConverter.get_next_bar_time(15)
        
        minutes = self.seconds_until_next // 60
        secs = self.seconds_until_next % 60
        countdown_str = f"{minutes:02d}:{secs:02d}"
        self.countdown_update.emit(countdown_str)
    
    def schedule_next_analysis(self):
        if not self.running:
            return
        
        self.next_bar_time, seconds = TimeZoneConverter.get_next_bar_time(15)
        self.log_signal.emit(f"下一个分析时间: {self.next_bar_time.strftime('%H:%M:%S')} (UTC0)", "info")
        
        # 在K线结束1秒后触发分析
        if self.analysis_timer:
            trigger_ms = (seconds + 1) * 1000
            self.analysis_timer.start(trigger_ms)
    
    def perform_analysis(self):
        if not self.running:
            return
        
        self.is_analyzing = True
        self.status_update.emit("正在分析...")
        
        self.log_signal.emit("=" * 50, "info")
        self.log_signal.emit(f"M15 K线结束，开始分析", "success")
        
        time_info = TimeZoneConverter.format_time_info()
        self.log_signal.emit(f"UTC0: {time_info['utc0_str']} | 北京: {time_info['beijing_str']}", "info")
        
        # 更新持仓
        try:
            positions = self.trader.get_positions()
            self.positions_update.emit(positions)
        except Exception as e:
            self.log_signal.emit(f"获取持仓失败: {str(e)}", "error")
        
        current_utc0 = TimeZoneConverter.get_current_utc0_time()
        current_hour_utc0 = current_utc0.hour
        
        # 检查交易时间
        if current_hour_utc0 >= StrategyConfig.FORCE_CLOSE_HOUR_UTC0:
            self.log_signal.emit("当前处于强制平仓时段，跳过分析", "warning")
            self.finish_analysis()
            return
        
        if current_hour_utc0 < StrategyConfig.TRADE_START_HOUR_UTC0 or current_hour_utc0 >= StrategyConfig.TRADE_END_HOUR_UTC0:
            self.log_signal.emit("非交易时段，跳过分析", "warning")
            self.finish_analysis()
            return
        
        # 分析各货币对
        signals_found = 0
        for symbol in self.enabled_symbols:
            if symbol not in self.analyzers:
                continue
            
            try:
                latest_bar_time = self.trader.get_latest_bar_time(symbol, mt5.TIMEFRAME_M15)
                if latest_bar_time is None:
                    continue
                
                if self.last_bar_times.get(symbol) == latest_bar_time:
                    continue
                
                df = self.trader.get_historical_data(symbol, mt5.TIMEFRAME_M15, 500)
                if df is None:
                    continue
                
                analyzer = self.analyzers[symbol]
                signal = analyzer.analyze(df, current_utc0)
                self.last_bar_times[symbol] = latest_bar_time
                
                if signal:
                    signals_found += 1
                    self.log_signal.emit(f"✅ {symbol}: {signal['direction']}信号!", "success")
                    
                    if self.trader.has_position(symbol, signal['direction']):
                        self.log_signal.emit(f"  ⚠️ 已有同方向持仓，跳过", "warning")
                    else:
                        self.signal_detected.emit(signal)
                else:
                    self.log_signal.emit(f"{symbol}: 无信号", "info")
                    
            except Exception as e:
                self.log_signal.emit(f"❌ {symbol}分析出错: {str(e)}", "error")
        
        if signals_found == 0:
            self.log_signal.emit("本轮分析无交易信号", "info")
        
        self.finish_analysis()
    
    def finish_analysis(self):
        self.is_analyzing = False
        if self.running:
            self.status_update.emit("等待下一个K线...")
            self.schedule_next_analysis()


class MainWindow(QMainWindow):
    """主窗口"""
    
    def __init__(self):
        super().__init__()
        self.worker_thread = None
        self.worker = None
        self.signal_history = deque(maxlen=50)
        self.init_ui()
        
    def init_ui(self):
        self.setWindowTitle("鱼身突破策略 - 自动交易系统 v1.5")
        self.setGeometry(100, 100, 1300, 800)
        
        # 深色主题
        self.setStyleSheet("""
            QMainWindow { background-color: #1e1e1e; }
            QLabel { color: #cccccc; font-size: 12px; }
            QPushButton {
                background-color: #3a3a3a;
                color: #ffffff;
                border: 1px solid #4a4a4a;
                padding: 6px 12px;
                border-radius: 3px;
                font-size: 12px;
                font-weight: bold;
            }
            QPushButton:hover { background-color: #4a4a4a; }
            QPushButton#startBtn {
                background-color: #2ecc71;
                color: white;
            }
            QPushButton#startBtn:hover { background-color: #27ae60; }
            QPushButton#stopBtn {
                background-color: #e74c3c;
                color: white;
            }
            QPushButton#stopBtn:hover { background-color: #c0392b; }
            QTableWidget {
                background-color: #2d2d2d;
                color: #cccccc;
                gridline-color: #3a3a3a;
                font-size: 11px;
                border: 1px solid #3a3a3a;
            }
            QTableWidget::item { padding: 3px; }
            QTableWidget::item:selected { background-color: #4a6da7; }
            QHeaderView::section {
                background-color: #3a3a3a;
                color: #cccccc;
                padding: 5px;
                border: 1px solid #4a4a4a;
                font-weight: bold;
            }
            QTextEdit {
                background-color: #1e1e1e;
                color: #d4d4d4;
                border: 1px solid #3a3a3a;
                font-family: Consolas, monospace;
                font-size: 11px;
            }
            QGroupBox {
                color: #cccccc;
                border: 1px solid #3a3a3a;
                border-radius: 4px;
                margin-top: 10px;
                font-weight: bold;
                font-size: 12px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px;
            }
            QCheckBox { color: #cccccc; }
        """)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        main_layout.setSpacing(8)
        main_layout.setContentsMargins(10, 10, 10, 10)
        
        # ===== 顶部状态栏 =====
        top_bar = QHBoxLayout()
        
        # 状态标签
        self.status_label = QLabel("⚪ 未启动")
        self.status_label.setStyleSheet("font-size: 14px; font-weight: bold;")
        top_bar.addWidget(self.status_label)
        
        top_bar.addStretch()
        
        # 时间显示
        self.time_label = QLabel("")
        self.time_label.setStyleSheet("font-size: 12px; padding: 4px 10px; background-color: #2d2d2d; border-radius: 3px;")
        top_bar.addWidget(self.time_label)
        
        # 倒计时
        countdown_label = QLabel("下个K线:")
        countdown_label.setStyleSheet("color: #888888;")
        top_bar.addWidget(countdown_label)
        
        self.countdown_label = QLabel("--:--")
        self.countdown_label.setStyleSheet("font-size: 14px; font-weight: bold; color: #3498db; padding: 4px 10px; background-color: #2d2d2d; border-radius: 3px;")
        top_bar.addWidget(self.countdown_label)
        
        # 控制按钮
        self.start_btn = QPushButton("▶ 启动")
        self.start_btn.setObjectName("startBtn")
        self.start_btn.clicked.connect(self.start_trading)
        top_bar.addWidget(self.start_btn)
        
        self.stop_btn = QPushButton("⏹ 停止")
        self.stop_btn.setObjectName("stopBtn")
        self.stop_btn.setEnabled(False)
        self.stop_btn.clicked.connect(self.stop_trading)
        top_bar.addWidget(self.stop_btn)
        
        main_layout.addLayout(top_bar)
        
        # ===== 货币对选择 =====
        symbols_layout = QHBoxLayout()
        symbols_layout.addWidget(QLabel("货币对:"))
        
        self.symbol_checkboxes = {}
        for symbol in CURRENCY_PAIRS.keys():
            cb = QCheckBox(symbol)
            cb.setChecked(True)
            cb.stateChanged.connect(self.on_symbol_changed)
            self.symbol_checkboxes[symbol] = cb
            symbols_layout.addWidget(cb)
        
        symbols_layout.addStretch()
        main_layout.addLayout(symbols_layout)
        
        # ===== 主内容区 =====
        main_splitter = QSplitter(Qt.Vertical)
        
        # 持仓区
        positions_widget = QWidget()
        positions_layout = QVBoxLayout(positions_widget)
        positions_layout.setContentsMargins(0, 0, 0, 0)
        
        positions_label = QLabel("📊 当前持仓")
        positions_label.setStyleSheet("font-size: 13px; font-weight: bold; padding: 5px;")
        positions_layout.addWidget(positions_label)
        
        self.positions_table = QTableWidget()
        self.positions_table.setColumnCount(7)
        self.positions_table.setHorizontalHeaderLabels([
            "货币对", "方向", "手数", "开仓价", "当前价", "止损/止盈", "浮动盈亏"
        ])
        self.positions_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.positions_table.setMaximumHeight(150)
        positions_layout.addWidget(self.positions_table)
        
        main_splitter.addWidget(positions_widget)
        
        # 下半部分：日志和历史信号（水平分割）
        bottom_splitter = QSplitter(Qt.Horizontal)
        
        # 日志区
        log_widget = QWidget()
        log_layout = QVBoxLayout(log_widget)
        log_layout.setContentsMargins(0, 0, 0, 0)
        
        log_label = QLabel("📝 系统日志")
        log_label.setStyleSheet("font-size: 13px; font-weight: bold; padding: 5px;")
        log_layout.addWidget(log_label)
        
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        log_layout.addWidget(self.log_text)
        
        bottom_splitter.addWidget(log_widget)
        
        # 历史信号区
        history_widget = QWidget()
        history_layout = QVBoxLayout(history_widget)
        history_layout.setContentsMargins(0, 0, 0, 0)
        
        history_label = QLabel("📈 历史信号")
        history_label.setStyleSheet("font-size: 13px; font-weight: bold; padding: 5px;")
        history_layout.addWidget(history_label)
        
        self.history_table = QTableWidget()
        self.history_table.setColumnCount(5)
        self.history_table.setHorizontalHeaderLabels(["时间", "货币对", "方向", "入场价", "状态"])
        self.history_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        history_layout.addWidget(self.history_table)
        
        bottom_splitter.addWidget(history_widget)
        
        bottom_splitter.setSizes([700, 400])
        main_splitter.addWidget(bottom_splitter)
        
        main_splitter.setSizes([150, 550])
        main_layout.addWidget(main_splitter)
        
        # 初始化定时器
        self.time_timer = QTimer()
        self.time_timer.timeout.connect(self.update_time_display)
        self.time_timer.start(1000)
        
        self.update_time_display()
    
    def on_symbol_changed(self):
        enabled = []
        for symbol, cb in self.symbol_checkboxes.items():
            if cb.isChecked():
                enabled.append(symbol)
        self.log(f"启用货币对: {', '.join(enabled) if enabled else '无'}", "info")
    
    def update_time_display(self):
        time_info = TimeZoneConverter.format_time_info()
        
        current_utc0_hour = time_info['utc0_hour']
        
        if current_utc0_hour >= StrategyConfig.FORCE_CLOSE_HOUR_UTC0:
            status = "强制平仓"
            status_color = "#e74c3c"
        elif current_utc0_hour < StrategyConfig.TRADE_START_HOUR_UTC0 or current_utc0_hour >= StrategyConfig.TRADE_END_HOUR_UTC0:
            status = "非交易"
            status_color = "#f39c12"
        else:
            status = "交易时段"
            status_color = "#2ecc71"
        
        display_text = f"UTC0 {time_info['utc0_str']} | 北京 {time_info['beijing_str']} | {status}"
        self.time_label.setText(display_text)
        self.time_label.setStyleSheet(f"font-size: 12px; padding: 4px 10px; background-color: #2d2d2d; border-radius: 3px; color: {status_color};")
    
    def update_countdown(self, countdown_str):
        self.countdown_label.setText(countdown_str)
    
    def update_positions(self, positions):
        self.positions_table.setRowCount(len(positions))
        
        for row, pos in enumerate(positions):
            self.positions_table.setItem(row, 0, QTableWidgetItem(pos['symbol']))
            
            direction_item = QTableWidgetItem("买入" if pos['type'] == 'buy' else "卖出")
            if pos['type'] == 'buy':
                direction_item.setForeground(QBrush(QColor("#2ecc71")))
            else:
                direction_item.setForeground(QBrush(QColor("#e74c3c")))
            self.positions_table.setItem(row, 1, direction_item)
            
            self.positions_table.setItem(row, 2, QTableWidgetItem(f"{pos['volume']:.2f}"))
            self.positions_table.setItem(row, 3, QTableWidgetItem(f"{pos['open_price']:.5f}"))
            self.positions_table.setItem(row, 4, QTableWidgetItem(f"{pos['current_price']:.5f}"))
            
            sl_tp = f"SL:{pos['sl']:.5f}" if pos['sl'] else "-"
            if pos['tp']:
                sl_tp += f" TP:{pos['tp']:.5f}"
            self.positions_table.setItem(row, 5, QTableWidgetItem(sl_tp))
            
            profit_item = QTableWidgetItem(f"${pos['profit']:.2f}")
            if pos['profit'] > 0:
                profit_item.setForeground(QBrush(QColor("#2ecc71")))
            elif pos['profit'] < 0:
                profit_item.setForeground(QBrush(QColor("#e74c3c")))
            self.positions_table.setItem(row, 6, profit_item)
    
    def add_signal_to_history(self, signal, status="待执行"):
        self.signal_history.append({
            'time': signal['time'],
            'symbol': signal['symbol'],
            'direction': signal['direction'],
            'entry_price': signal['entry_price'],
            'status': status
        })
        self.refresh_history_table()
    
    def refresh_history_table(self):
        self.history_table.setRowCount(len(self.signal_history))
        
        for row, signal in enumerate(reversed(self.signal_history)):
            self.history_table.setItem(row, 0, QTableWidgetItem(signal['time'].strftime("%H:%M:%S")))
            self.history_table.setItem(row, 1, QTableWidgetItem(signal['symbol']))
            
            direction_item = QTableWidgetItem("买入" if signal['direction'] == 'buy' else "卖出")
            if signal['direction'] == 'buy':
                direction_item.setForeground(QBrush(QColor("#2ecc71")))
            else:
                direction_item.setForeground(QBrush(QColor("#e74c3c")))
            self.history_table.setItem(row, 2, direction_item)
            
            self.history_table.setItem(row, 3, QTableWidgetItem(f"{signal['entry_price']:.5f}"))
            
            status_item = QTableWidgetItem(signal['status'])
            if signal['status'] == "已下单" or signal['status'] == "执行中":
                status_item.setForeground(QBrush(QColor("#2ecc71")))
            elif signal['status'] == "已取消":
                status_item.setForeground(QBrush(QColor("#f39c12")))
            elif signal['status'] == "失败":
                status_item.setForeground(QBrush(QColor("#e74c3c")))
            self.history_table.setItem(row, 4, status_item)
    
    def log(self, message, msg_type="info"):
        colors = {
            "info": "#d4d4d4",
            "warning": "#f39c12",
            "error": "#e74c3c",
            "success": "#2ecc71"
        }
        
        color = colors.get(msg_type, "#d4d4d4")
        timestamp = datetime.now().strftime("%H:%M:%S")
        
        formatted_msg = f'<span style="color: #888888;">[{timestamp}]</span> <span style="color: {color};">{message}</span>'
        
        self.log_text.append(formatted_msg)
        self.log_text.moveCursor(QTextCursor.End)
    
    def on_signal_detected(self, signal):
        """信号处理 - 自动执行下单，无需人工确认"""
        self.add_signal_to_history(signal, "执行中")
        self.log(f"{signal['symbol']}: 检测到{signal['direction']}信号，自动执行下单...", "success")
        self.execute_trade(signal)
    
    def execute_trade(self, signal):
        trader = MTTrader()
        if trader.connect()[0]:
            success, msg = trader.place_order(signal)
            trader.disconnect()
            
            if success:
                self.log(f"{signal['symbol']}: {msg}", "success")
                # 更新历史记录状态
                if self.signal_history:
                    self.signal_history[-1]['status'] = "已下单"
            else:
                self.log(f"{signal['symbol']}: {msg}", "error")
                if self.signal_history:
                    self.signal_history[-1]['status'] = "失败"
            
            self.refresh_history_table()
    
    def start_trading(self):
        enabled_symbols = []
        for symbol, cb in self.symbol_checkboxes.items():
            if cb.isChecked():
                enabled_symbols.append(symbol)
        
        if not enabled_symbols:
            QMessageBox.warning(self, "警告", "请至少选择一个货币对")
            return
        
        self.worker_thread = QThread()
        self.worker = AnalysisWorker(enabled_symbols)
        self.worker.moveToThread(self.worker_thread)
        
        self.worker.log_signal.connect(self.log)
        self.worker.signal_detected.connect(self.on_signal_detected)
        self.worker.positions_update.connect(self.update_positions)
        self.worker.countdown_update.connect(self.update_countdown)
        
        self.worker_thread.started.connect(self.worker.start)
        self.worker_thread.start()
        
        self.status_label.setText("🟢 运行中")
        self.status_label.setStyleSheet("font-size: 14px; font-weight: bold; color: #2ecc71;")
        self.start_btn.setEnabled(False)
        self.stop_btn.setEnabled(True)
        
        for cb in self.symbol_checkboxes.values():
            cb.setEnabled(False)
        
        self.log("交易系统已启动（自动下单模式）", "success")
    
    def stop_trading(self):
        if self.worker:
            self.worker.stop()
            self.worker_thread.quit()
            self.worker_thread.wait()
            self.worker = None
            self.worker_thread = None
        
        self.status_label.setText("⚪ 已停止")
        self.status_label.setStyleSheet("font-size: 14px; font-weight: bold; color: #888888;")
        self.start_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)
        
        for cb in self.symbol_checkboxes.values():
            cb.setEnabled(True)
        
        self.countdown_label.setText("--:--")
        self.log("交易系统已停止", "warning")
    
    def closeEvent(self, event):
        if self.worker_thread and self.worker_thread.isRunning():
            reply = QMessageBox.question(
                self,
                "确认退出",
                "交易系统正在运行中，确定要退出吗？",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No
            )
            
            if reply == QMessageBox.Yes:
                self.stop_trading()
                event.accept()
            else:
                event.ignore()
        else:
            event.accept()


def main():
    app = QApplication(sys.argv)
    app.setApplicationName("鱼身突破策略自动交易系统")
    app.setApplicationVersion("1.5")
    
    window = MainWindow()
    window.show()
    
    window.log("=" * 50, "info")
    window.log("鱼身突破策略自动交易系统 v1.5 启动", "success")
    window.log("分析模式: 每个M15 K线结束时触发", "info")
    window.log("下单模式: 自动执行，无需人工确认", "info")
    
    time_info = TimeZoneConverter.format_time_info()
    window.log(f"UTC0: {time_info['utc0_str']} | 北京: {time_info['beijing_str']}", "info")
    
    next_bar_time, seconds = TimeZoneConverter.get_next_bar_time(15)
    window.log(f"下一个M15 K线: {next_bar_time.strftime('%H:%M:%S')} (UTC0)", "info")
    window.log("=" * 50, "info")
    
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()