import time

from tqdm import tqdm
from loguru import logger
from bit_tools import openBrowser
from DrissionPage import ChromiumPage
from DrissionPage import ChromiumOptions

from bitmart.api_contract import APIContract


class BitmartFuturesTransaction:
    def __init__(self, bit_id):

        self.page: ChromiumPage | None = None

        self.api_key = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
        self.secret_key = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
        self.memo = "合约交易"

        self.contract_symbol = "ETHUSDT"

        self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15))

        self.start = 0  # 持仓状态: -1 空, 0 无, 1 多

        self.pbar = tqdm(total=30, desc="等待K线", ncols=80)  # 可选：用于长时间等待时展示进度

        self.last_kline_time = None  # 上一次处理的K线时间戳，用于判断是否是新K线

        # 反手频率控制
        self.reverse_cooldown_seconds = 1.5 * 60  # 反手冷却时间（秒）
        self.reverse_min_move_pct = 0.05  # 反手最小价差过滤（百分比）
        self.last_reverse_time = None  # 上次反手时间

        # 开仓频率控制
        self.open_cooldown_seconds = 60  # 开仓冷却时间（秒），两次开仓至少间隔此时长
        self.last_open_time = None  # 上次开仓时间
        self.last_open_kline_id = None  # 上次开仓所在 K 线 id，同一根 K 线只允许开仓一次

        self.leverage = "100"  # 高杠杆（全仓模式下可开更大仓位）
        self.open_type = "cross"  # 全仓模式
        self.risk_percent = 0  # 未使用；若启用则可为每次开仓占可用余额的百分比
        self.take_profit_usd = 5  # 仓位盈利达到此金额（美元）时平仓止盈
        self.stop_loss_usd = -3  # 固定止损：亏损达到 3 美元平仓
        self.trailing_activation_usd = 2  # 盈利达到此金额后启动移动止损
        self.trailing_distance_usd = 1.5  # 从最高盈利回撤此金额则平仓
        self.max_unrealized_pnl_seen = None  # 持仓期间见过的最大盈利（用于移动止损）

        self.open_avg_price = None  # 开仓价格
        self.current_amount = None  # 持仓量

        self.bit_id = bit_id
        self.default_order_size = 25  # 开仓/反手张数，统一在此修改

        # 策略相关变量
        self.prev_kline = None  # 上一根K线
        self.current_kline = None  # 当前K线
        self.prev_entity = None  # 上一根K线实体大小
        self.current_open = None  # 当前K线开盘价

    def get_klines(self):
        """获取最近2根K线（当前K线和上一根K线）"""
        try:
            end_time = int(time.time())
            # 获取足够多的条目确保有最新的K线
            response = self.contractAPI.get_kline(
                contract_symbol=self.contract_symbol,
                step=5,  # 5分钟
                start_time=end_time - 3600 * 3,  # 取最近3小时
                end_time=end_time
            )[0]["data"]

            # 每根: [timestamp, open, high, low, close, volume]
            formatted = []
            for k in response:
                formatted.append({
                    'id': int(k["timestamp"]),
                    'open': float(k["open_price"]),
                    'high': float(k["high_price"]),
                    'low': float(k["low_price"]),
                    'close': float(k["close_price"])
                })
            formatted.sort(key=lambda x: x['id'])

            # 返回最近2根K线：倒数第二根（上一根）和最后一根（当前）
            if len(formatted) >= 2:
                return formatted[-2], formatted[-1]
            return None, None
        except Exception as e:
            logger.error(f"获取K线异常: {e}")
            self.ding(text="获取K线异常", error=True)
            return None, None

    def get_current_price(self):
        """获取当前最新价格"""
        try:
            end_time = int(time.time())
            response = self.contractAPI.get_kline(
                contract_symbol=self.contract_symbol,
                step=1,  # 1分钟
                start_time=end_time - 3600 * 1,  # 取最近1小时
                end_time=end_time
            )[0]
            if response['code'] == 1000:
                return float(response['data'][-1]["close_price"])
            return None
        except Exception as e:
            logger.error(f"获取价格异常: {e}")
            return None

    def get_available_balance(self):
        """获取合约账户可用USDT余额"""
        try:
            response = self.contractAPI.get_assets_detail()[0]
            if response['code'] == 1000:
                data = response['data']
                if isinstance(data, dict):
                    return float(data.get('available_balance', 0))
                elif isinstance(data, list):
                    for asset in data:
                        if asset.get('currency') == 'USDT':
                            return float(asset.get('available_balance', 0))
            return None
        except Exception as e:
            logger.error(f"余额查询异常: {e}")
            return None

    def get_position_status(self):
        """获取当前持仓方向"""
        try:
            response = self.contractAPI.get_position(contract_symbol=self.contract_symbol)[0]
            if response['code'] == 1000:
                positions = response['data']
                if not positions:
                    self.start = 0
                    self.open_avg_price = None
                    self.current_amount = None
                    self.unrealized_pnl = None
                    return True
                pos = positions[0]
                self.start = 1 if pos['position_type'] == 1 else -1
                self.open_avg_price = float(pos['open_avg_price'])
                self.current_amount = float(pos['current_amount'])
                self.position_cross = pos["position_cross"]
                # 直接从API获取未实现盈亏（Bitmart返回的是 unrealized_value 字段）
                self.unrealized_pnl = float(pos.get('unrealized_value', 0))
                logger.debug(f"持仓详情: 方向={self.start}, 开仓均价={self.open_avg_price}, "
                             f"持仓量={self.current_amount}, 未实现盈亏={self.unrealized_pnl:.2f}")
                return True
            else:
                return False
        except Exception as e:
            logger.error(f"持仓查询异常: {e}")
            return False

    def get_unrealized_pnl_usd(self):
        """
        获取当前持仓未实现盈亏（美元），直接使用API返回值
        """
        if self.start == 0 or self.unrealized_pnl is None:
            return None
        return self.unrealized_pnl

    def set_leverage(self):
        """程序启动时设置全仓 + 高杠杆"""
        try:
            response = self.contractAPI.post_submit_leverage(
                contract_symbol=self.contract_symbol,
                leverage=self.leverage,
                open_type=self.open_type
            )[0]
            if response['code'] == 1000:
                logger.success(f"全仓模式 + {self.leverage}x 杠杆设置成功")
                return True
            else:
                logger.error(f"杠杆设置失败: {response}")
                return False
        except Exception as e:
            logger.error(f"设置杠杆异常: {e}")
            return False

    def openBrowser(self):
        """打开 TGE 对应浏览器实例"""
        try:
            bit_port = openBrowser(id=self.bit_id)
            co = ChromiumOptions()
            co.set_local_port(port=bit_port)
            self.page = ChromiumPage(addr_or_opts=co)
            return True
        except:
            return False

    def click_safe(self, xpath, sleep=0.5):
        """安全点击"""
        try:
            ele = self.page.ele(xpath)
            if not ele:
                return False
            # ele.scroll.to_see(center=True)
            # time.sleep(sleep)
            ele.click(by_js=True)
            return True
        except:
            return False

    def 平仓(self):
        """平仓操作"""
        self.click_safe('x://span[normalize-space(text()) ="市价"]')

    def 开单(self, marketPriceLongOrder=0, limitPriceShortOrder=0, size=None, price=None):
        """
        marketPriceLongOrder  市价做多或者做空，1是做多，-1是做空
        limitPriceShortOrder  限价做多或者做空
        """
        if marketPriceLongOrder == -1:
            # self.click_safe('x://button[normalize-space(text()) ="市价"]')
            # self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
            self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
        elif marketPriceLongOrder == 1:
            # self.click_safe('x://button[normalize-space(text()) ="市价"]')
            # self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
            self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')

        if limitPriceShortOrder == -1:
            self.click_safe('x://button[normalize-space(text()) ="限价"]')
            self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
            time.sleep(1)
            self.page.ele('x://*[@id="size_0"]').input(1)
            self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
        elif limitPriceShortOrder == 1:
            self.click_safe('x://button[normalize-space(text()) ="限价"]')
            self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
            time.sleep(1)
            self.page.ele('x://*[@id="size_0"]').input(1)
            self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')

    def ding(self, text, error=False):
        """日志通知"""
        if error:
            logger.error(text)
        else:
            logger.info(text)

    def calculate_entity(self, kline):
        """计算K线实体大小（绝对值）"""
        return abs(kline['close'] - kline['open'])

    def calculate_upper_shadow(self, kline):
        """计算上阴线（上影线）涨幅百分比"""
        # 上阴线 = (最高价 - max(开盘价, 收盘价)) / max(开盘价, 收盘价)
        body_top = max(kline['open'], kline['close'])
        if body_top == 0:
            return 0
        return (kline['high'] - body_top) / body_top * 100

    def calculate_lower_shadow(self, kline):
        """计算下阴线（下影线）跌幅百分比"""
        # 下阴线 = (min(开盘价, 收盘价) - 最低价) / min(开盘价, 收盘价)
        body_bottom = min(kline['open'], kline['close'])
        if body_bottom == 0:
            return 0
        return (body_bottom - kline['low']) / body_bottom * 100

    def get_entity_edge(self, kline):
        """获取K线实体边（收盘价或开盘价，取决于是阳线还是阴线）"""
        # 阳线（收盘>开盘）：实体上边=收盘价，实体下边=开盘价
        # 阴线（收盘<开盘）：实体上边=开盘价，实体下边=收盘价
        return {
            'upper': max(kline['open'], kline['close']),  # 实体上边
            'lower': min(kline['open'], kline['close'])  # 实体下边
        }

    def check_signal(self, current_price, prev_kline, current_kline):
        """
        检查交易信号
        返回: ('long', trigger_price) / ('short', trigger_price) / None
        """
        # 计算上一根K线实体
        prev_entity = self.calculate_entity(prev_kline)

        # 实体过小不交易（实体 < 0.1）
        if prev_entity < 0.1:
            logger.info(f"上一根K线实体过小: {prev_entity:.4f}，跳过信号检测")
            return None

        # 获取上一根K线的实体上下边
        prev_entity_edge = self.get_entity_edge(prev_kline)
        prev_entity_upper = prev_entity_edge['upper']  # 实体上边
        prev_entity_lower = prev_entity_edge['lower']  # 实体下边

        # 优化：以下两种情况以当前这根的开盘价作为计算基准
        # 1) 上一根阳线 且 当前开盘价 > 上一根收盘价（跳空高开）
        # 2) 上一根阴线 且 当前开盘价 < 上一根收盘价（跳空低开）
        prev_is_bullish_for_calc = prev_kline['close'] > prev_kline['open']
        prev_is_bearish_for_calc = prev_kline['close'] < prev_kline['open']
        current_open_above_prev_close = current_kline['open'] > prev_kline['close']
        current_open_below_prev_close = current_kline['open'] < prev_kline['close']
        use_current_open_as_base = (prev_is_bullish_for_calc and current_open_above_prev_close) or (prev_is_bearish_for_calc and current_open_below_prev_close)

        if use_current_open_as_base:
            # 以当前K线开盘价为基准计算（跳空时用当前开盘价参与计算）
            calc_lower = current_kline['open']
            calc_upper = current_kline['open']  # 同一基准，上下四分之一对称
            long_trigger = calc_lower + prev_entity / 4
            short_trigger = calc_upper - prev_entity / 4
            long_breakout = calc_upper + prev_entity / 4
            short_breakout = calc_lower - prev_entity / 4
        else:
            # 原有计算方式
            long_trigger = prev_entity_lower + prev_entity / 4  # 做多触发价 = 实体下边 + 实体/4（下四分之一处）
            short_trigger = prev_entity_upper - prev_entity / 4  # 做空触发价 = 实体上边 - 实体/4（上四分之一处）
            long_breakout = prev_entity_upper + prev_entity / 4  # 做多突破价 = 实体上边 + 实体/4
            short_breakout = prev_entity_lower - prev_entity / 4  # 做空突破价 = 实体下边 - 实体/4

        # 上一根阴线 + 当前阳线：做多形态，不按上一根K线上三分之一做空
        prev_is_bearish = prev_kline['close'] < prev_kline['open']
        current_is_bullish = current_kline['close'] > current_kline['open']
        skip_short_by_upper_third = prev_is_bearish and current_is_bullish
        # 上一根阳线 + 当前阴线：做空形态，不按上一根K线下三分之一做多
        prev_is_bullish = prev_kline['close'] > prev_kline['open']
        current_is_bearish = current_kline['close'] < current_kline['open']
        skip_long_by_lower_third = prev_is_bullish and current_is_bearish

        if use_current_open_as_base:
            if prev_is_bullish_for_calc and current_open_above_prev_close:
                logger.info(f"上一根阳线且当前开盘价({current_kline['open']:.2f})>上一根收盘价({prev_kline['close']:.2f})，以当前开盘价为基准计算")
            else:
                logger.info(f"上一根阴线且当前开盘价({current_kline['open']:.2f})<上一根收盘价({prev_kline['close']:.2f})，以当前开盘价为基准计算")
        logger.info(f"当前价格: {current_price:.2f}, 上一根实体: {prev_entity:.4f}")
        logger.info(f"上一根实体上边: {prev_entity_upper:.2f}, 下边: {prev_entity_lower:.2f}")
        logger.info(f"做多触发价(下1/4): {long_trigger:.2f}, 做空触发价(上1/4): {short_trigger:.2f}")
        logger.info(f"突破做多价(上1/4外): {long_breakout:.2f}, 突破做空价(下1/4外): {short_breakout:.2f}")
        if skip_short_by_upper_third:
            logger.info("上一根阴线+当前阳线(做多形态)，不按上四分之一做空")
        if skip_long_by_lower_third:
            logger.info("上一根阳线+当前阴线(做空形态)，不按下四分之一做多")

        # 无持仓时检查开仓信号
        if self.start == 0:
            if current_price >= long_breakout and not skip_long_by_lower_third:
                logger.info(f"触发做多信号！价格 {current_price:.2f} >= 突破价(上1/4外) {long_breakout:.2f}")
                return ('long', long_breakout)
            elif current_price <= short_breakout and not skip_short_by_upper_third:
                logger.info(f"触发做空信号！价格 {current_price:.2f} <= 突破价(下1/4外) {short_breakout:.2f}")
                return ('short', short_breakout)

        # 持仓时检查反手信号
        elif self.start == 1:  # 持多仓
            # 反手条件1: 价格跌到上一根K线的上三分之一处（做空触发价）；上一根阴线+当前阳线做多时跳过
            if current_price <= short_trigger and not skip_short_by_upper_third:
                logger.info(f"持多反手做空！价格 {current_price:.2f} <= 触发价(上1/4) {short_trigger:.2f}")
                return ('reverse_short', short_trigger)

            # 反手条件2: 上一根K线上阴线涨幅>0.01%，当前跌到上一根实体下边
            upper_shadow_pct = self.calculate_upper_shadow(prev_kline)
            if upper_shadow_pct > 0.01 and current_price <= prev_entity_lower:
                logger.info(f"持多反手做空！上阴线涨幅 {upper_shadow_pct:.4f}% > 0.01%，"
                            f"价格 {current_price:.2f} <= 实体下边 {prev_entity_lower:.2f}")
                return ('reverse_short', prev_entity_lower)

        elif self.start == -1:  # 持空仓
            # 反手条件1: 价格涨到上一根K线的下三分之一处（做多触发价）；上一根阳线+当前阴线做空时跳过
            if current_price >= long_trigger and not skip_long_by_lower_third:
                logger.info(f"持空反手做多！价格 {current_price:.2f} >= 触发价(下1/4) {long_trigger:.2f}")
                return ('reverse_long', long_trigger)

            # 反手条件2: 上一根K线下阴线跌幅>0.01%，当前涨到上一根实体上边
            lower_shadow_pct = self.calculate_lower_shadow(prev_kline)
            if lower_shadow_pct > 0.01 and current_price >= prev_entity_upper:
                logger.info(f"持空反手做多！下阴线跌幅 {lower_shadow_pct:.4f}% > 0.01%，"
                            f"价格 {current_price:.2f} >= 实体上边 {prev_entity_upper:.2f}")
                return ('reverse_long', prev_entity_upper)

        return None

    def can_open(self, current_kline_id):
        """开仓前过滤：同一根 K 线只开一次 + 开仓冷却时间。仅用于 long/short 新开仓。"""
        now = time.time()
        if self.last_open_kline_id is not None and self.last_open_kline_id == current_kline_id:
            logger.info(f"开仓频率控制：本 K 线({current_kline_id})已开过仓，跳过")
            return False
        if self.last_open_time is not None and now - self.last_open_time < self.open_cooldown_seconds:
            remain = self.open_cooldown_seconds - (now - self.last_open_time)
            logger.info(f"开仓冷却中，剩余 {remain:.0f} 秒")
            return False
        return True

    def can_reverse(self, current_price, trigger_price):
        """反手前过滤：冷却时间 + 最小价差"""
        now = time.time()
        if self.last_reverse_time and now - self.last_reverse_time < self.reverse_cooldown_seconds:
            remain = self.reverse_cooldown_seconds - (now - self.last_reverse_time)
            logger.info(f"反手冷却中，剩余 {remain:.0f} 秒")
            return False

        if trigger_price and trigger_price > 0:
            move_pct = abs(current_price - trigger_price) / trigger_price * 100
            if move_pct < self.reverse_min_move_pct:
                logger.info(f"反手价差不足: {move_pct:.4f}% < {self.reverse_min_move_pct}%")
                return False

        return True

    def verify_no_position(self, max_retries=5, retry_interval=3):
        """
        验证当前无持仓
        返回: True 表示无持仓可以开仓，False 表示有持仓不能开仓
        """
        for i in range(max_retries):
            if self.get_position_status():
                if self.start == 0:
                    logger.info(f"确认无持仓，可以开仓")
                    return True
                else:
                    logger.warning(
                        f"仍有持仓 (方向: {self.start})，等待 {retry_interval} 秒后重试 ({i + 1}/{max_retries})")
                    time.sleep(retry_interval)
            else:
                logger.warning(f"查询持仓状态失败，等待 {retry_interval} 秒后重试 ({i + 1}/{max_retries})")
                time.sleep(retry_interval)

        logger.error(f"经过 {max_retries} 次重试仍有持仓或查询失败，放弃开仓")
        return False

    def verify_position_direction(self, expected_direction):
        """
        验证当前持仓方向是否与预期一致
        expected_direction: 1 多仓, -1 空仓
        返回: True 表示持仓方向正确，False 表示不正确
        """
        if self.get_position_status():
            if self.start == expected_direction:
                logger.info(f"持仓方向验证成功: {self.start}")
                return True
            else:
                logger.warning(f"持仓方向不符: 期望 {expected_direction}, 实际 {self.start}")
                return False
        else:
            logger.error("查询持仓状态失败")
            return False

    def execute_trade(self, signal, size=None):
        """执行交易。size 不传或为 None 时使用 default_order_size。"""
        signal_type, trigger_price = signal
        size = self.default_order_size if size is None else size

        if signal_type == 'long':
            # 开多前先确认无持仓
            logger.info(f"准备开多，触发价: {trigger_price:.2f}")
            if not self.get_position_status():
                logger.error("开仓前查询持仓状态失败，放弃开仓")
                return False
            if self.start != 0:
                logger.warning(f"开多前发现已有持仓 (方向: {self.start})，放弃开仓避免双向持仓")
                return False

            logger.info(f"确认无持仓，执行开多")
            self.开单(marketPriceLongOrder=1, size=size)
            time.sleep(3)  # 等待订单执行

            # 验证开仓是否成功
            if self.verify_position_direction(1):
                self.max_unrealized_pnl_seen = None  # 新仓位重置移动止损记录
                self.last_open_time = time.time()
                self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None)
                logger.success("开多成功")
                return True
            else:
                logger.error("开多后持仓验证失败")
                return False

        elif signal_type == 'short':
            # 开空前先确认无持仓
            logger.info(f"准备开空，触发价: {trigger_price:.2f}")
            if not self.get_position_status():
                logger.error("开仓前查询持仓状态失败，放弃开仓")
                return False
            if self.start != 0:
                logger.warning(f"开空前发现已有持仓 (方向: {self.start})，放弃开仓避免双向持仓")
                return False

            logger.info(f"确认无持仓，执行开空")
            self.开单(marketPriceLongOrder=-1, size=size)
            time.sleep(3)  # 等待订单执行

            # 验证开仓是否成功
            if self.verify_position_direction(-1):
                self.max_unrealized_pnl_seen = None  # 新仓位重置移动止损记录
                self.last_open_time = time.time()
                self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None)
                logger.success("开空成功")
                return True
            else:
                logger.error("开空后持仓验证失败")
                return False

        elif signal_type == 'reverse_long':
            # 平空 + 开多（反手做多）：先平仓，确认无仓后再开多，避免双向持仓
            logger.info(f"执行反手做多，触发价: {trigger_price:.2f}")
            self.平仓()
            time.sleep(1)  # 给交易所处理平仓的时间
            # 轮询确认已无持仓再开多（最多等约 10 秒）
            for _ in range(10):
                if self.get_position_status() and self.start == 0:
                    break
                time.sleep(1)
            if self.start != 0:
                logger.warning("反手做多：平仓后仍有持仓，放弃本次开多")
                return False
            logger.info("已确认无持仓，执行开多")
            self.开单(marketPriceLongOrder=1, size=size)
            time.sleep(3)

            if self.verify_position_direction(1):
                self.max_unrealized_pnl_seen = None
                logger.success("反手做多成功")
                self.last_reverse_time = time.time()
                time.sleep(20)
                return True
            else:
                logger.error("反手做多后持仓验证失败")
                return False

        elif signal_type == 'reverse_short':
            # 平多 + 开空（反手做空）：先平仓，确认无仓后再开空
            logger.info(f"执行反手做空，触发价: {trigger_price:.2f}")
            self.平仓()
            time.sleep(1)
            for _ in range(10):
                if self.get_position_status() and self.start == 0:
                    break
                time.sleep(1)
            if self.start != 0:
                logger.warning("反手做空：平仓后仍有持仓，放弃本次开空")
                return False
            logger.info("已确认无持仓，执行开空")
            self.开单(marketPriceLongOrder=-1, size=size)
            time.sleep(3)

            if self.verify_position_direction(-1):
                self.max_unrealized_pnl_seen = None
                logger.success("反手做空成功")
                self.last_reverse_time = time.time()
                time.sleep(20)
                return True
            else:
                logger.error("反手做空后持仓验证失败")
                return False

        return False

    def action(self):
        """主循环"""

        logger.info("开始运行四分之一策略交易...")

        # 启动时设置全仓高杠杆
        if not self.set_leverage():
            logger.error("杠杆设置失败，程序继续运行但可能下单失败")
            return

        page_start = True

        while True:

            if page_start:
                # 打开浏览器
                for i in range(5):
                    if self.openBrowser():
                        logger.info("浏览器打开成功")
                        break
                else:
                    self.ding("打开浏览器失败！", error=True)
                    return

                # 进入交易页面
                self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
                self.click_safe('x://button[normalize-space(text()) ="市价"]')

                self.page.ele('x://*[@id="size_0"]').input(vals=25, clear=True)

                page_start = False

            try:
                # 1. 获取K线数据（当前K线和上一根K线）
                prev_kline, current_kline = self.get_klines()
                if not prev_kline or not current_kline:
                    logger.warning("获取K线失败，等待重试...")
                    time.sleep(5)
                    continue

                # 记录进入新的K线
                current_kline_time = current_kline['id']
                if self.last_kline_time != current_kline_time:
                    self.last_kline_time = current_kline_time
                    logger.info(f"进入新K线: {current_kline_time}")

                # 2. 获取当前价格
                current_price = self.get_current_price()
                if not current_price:
                    logger.warning("获取价格失败，等待重试...")
                    time.sleep(2)
                    continue

                # 3. 每次循环都通过SDK获取真实持仓状态（避免状态不同步导致双向持仓）
                if not self.get_position_status():
                    logger.warning("获取持仓状态失败，等待重试...")
                    time.sleep(2)
                    continue

                logger.debug(f"当前持仓状态: {self.start} (0=无, 1=多, -1=空)")

                # 3.5 止损/止盈/移动止损
                if self.start != 0:
                    pnl_usd = self.get_unrealized_pnl_usd()
                    if pnl_usd is not None:
                        # 固定止损：亏损达到 3 美元平仓
                        if pnl_usd <= self.stop_loss_usd:
                            logger.info(f"仓位亏损 {pnl_usd:.2f} 美元 <= 止损 {self.stop_loss_usd} 美元，执行止损平仓")
                            self.平仓()
                            self.max_unrealized_pnl_seen = None
                            time.sleep(3)
                            continue
                        # 更新持仓期间最大盈利（用于移动止损）
                        if self.max_unrealized_pnl_seen is None:
                            self.max_unrealized_pnl_seen = pnl_usd
                        else:
                            self.max_unrealized_pnl_seen = max(self.max_unrealized_pnl_seen, pnl_usd)
                        # 移动止损：盈利曾达到 activation 后，从最高盈利回撤 trailing_distance 则平仓
                        if self.max_unrealized_pnl_seen >= self.trailing_activation_usd:
                            if pnl_usd < self.max_unrealized_pnl_seen - self.trailing_distance_usd:
                                logger.info(f"移动止损：当前盈利 {pnl_usd:.2f} 从最高 {self.max_unrealized_pnl_seen:.2f} 回撤 >= {self.trailing_distance_usd} 美元，平仓")
                                self.平仓()
                                self.max_unrealized_pnl_seen = None
                                time.sleep(3)
                                continue
                        # 止盈：盈利达到 take_profit_usd 平仓
                        if pnl_usd >= self.take_profit_usd:
                            logger.info(f"仓位盈利 {pnl_usd:.2f} 美元 >= {self.take_profit_usd} 美元，执行止盈平仓")
                            self.平仓()
                            self.max_unrealized_pnl_seen = None
                            time.sleep(3)
                            continue

                # 4. 检查信号
                signal = self.check_signal(current_price, prev_kline, current_kline)

                # 5. 反手过滤：冷却时间 + 最小价差
                if signal and signal[0].startswith('reverse_'):
                    if not self.can_reverse(current_price, signal[1]):
                        signal = None

                # 5.5 开仓频率过滤：同一根 K 线只开一次 + 开仓冷却
                if signal and signal[0] in ('long', 'short'):
                    if not self.can_open(current_kline_time):
                        signal = None
                    else:
                        self._current_kline_id_for_open = current_kline_time  # 供 execute_trade 成功后记录

                # 6. 有信号则执行交易
                if signal:
                    trade_success = self.execute_trade(signal)
                    if trade_success:
                        logger.success(f"交易执行完成: {signal[0]}, 当前持仓状态: {self.start}")
                        page_start = True
                    else:
                        logger.warning(f"交易执行失败或被阻止: {signal[0]}")

                # 短暂等待后继续循环（同一根K线遇到信号就操作）
                time.sleep(0.1)

                if page_start:
                    self.page.close()
                    time.sleep(5)

            except KeyboardInterrupt:
                logger.info("用户中断，程序退出")
                break
            except Exception as e:
                logger.error(f"主循环异常: {e}")
                time.sleep(5)


if __name__ == '__main__':
    BitmartFuturesTransaction(bit_id="f2320f57e24c45529a009e1541e25961").action()
