"""
BitMart 被动做市/高频刷单策略
核心逻辑：在盘口两侧不断挂单，赚取价差+返佣
使用浏览器自动化下单，获取高返佣
"""

import time
from loguru import logger
from threading import Lock
from dataclasses import dataclass
from bitmart.api_contract import APIContract
from typing import Optional, Dict, List, Tuple
from DrissionPage import ChromiumPage, ChromiumOptions

from bit_tools import openBrowser


# ================================================================
# 📊 配置类
# ================================================================

@dataclass
class MarketMakingConfig:
    bit_id: str = "f2320f57e24c45529a009e1541e25961"
    """做市策略配置"""
    # API配置（仅用于查询，不下单）
    api_key: str = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
    secret_key: str = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
    memo: str = "合约交易"
    contract_symbol: str = "ETHUSDT"

    # 浏览器配置
    tge_id: int = 196495  # TGE浏览器ID
    tge_url: str = "http://127.0.0.1:50326"
    tge_headers: Dict = None
    trading_url: str = "https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT"

    # 做市参数
    spread_percent: float = 0.01  # 价差百分比（0.01% = 买一卖一之间）
    order_size_usdt: float = 10.0  # 每单金额（USDT）
    max_position_usdt: float = 100.0  # 最大持仓金额（USDT）

    # 订单管理
    order_refresh_interval: float = 2.0  # 订单刷新间隔（秒）
    order_timeout: float = 60.0  # 订单超时时间（秒），超时后撤单重新挂

    # 风险控制
    max_daily_loss: float = 50.0  # 每日最大亏损（USDT）
    max_daily_trades: int = 1000  # 每日最大交易次数

    # 杠杆和模式
    leverage: str = "30"  # 杠杆倍数
    open_type: str = "cross"  # 全仓模式

    def __post_init__(self):
        """初始化TGE headers"""
        if self.tge_headers is None:
            self.tge_headers = {
                "Authorization": "Bearer asp_174003986c9b0799677c5b2c1adb76e402735d753bc91a91",
                "Content-Type": "application/json"
            }


# ================================================================
# 📊 订单簿数据结构
# ================================================================

@dataclass
class OrderBook:
    """订单簿数据"""
    bids: List[Tuple[float, float]]  # [(价格, 数量), ...] 买盘，价格从高到低
    asks: List[Tuple[float, float]]  # [(价格, 数量), ...] 卖盘，价格从低到高
    timestamp: float

    @property
    def best_bid(self) -> Optional[float]:
        """买一价"""
        return self.bids[0][0] if self.bids else None

    @property
    def best_ask(self) -> Optional[float]:
        """卖一价"""
        return self.asks[0][0] if self.asks else None

    @property
    def spread(self) -> Optional[float]:
        """价差"""
        if self.best_bid and self.best_ask:
            return self.best_ask - self.best_bid
        return None

    @property
    def mid_price(self) -> Optional[float]:
        """中间价"""
        if self.best_bid and self.best_ask:
            return (self.best_bid + self.best_ask) / 2
        return None


@dataclass
class PendingOrder:
    """pending订单信息"""
    order_id: str
    side: str  # "buy" or "sell"
    price: float
    size: float
    create_time: float
    status: str  # "pending", "filled", "cancelled"


# ================================================================
# 📊 浏览器管理器
# ================================================================

class BrowserManager:
    """浏览器管理器：负责浏览器的启动、接管和标签页管理"""

    def __init__(self, config: MarketMakingConfig, bit_id="f2320f57e24c45529a009e1541e25961"):
        self.bit_id = "f2320f57e24c45529a009e1541e25961"
        self.config = config
        self.tge_port: Optional[int] = None
        self.page: Optional[ChromiumPage] = None

    def open_browser(self) -> bool:
        """打开浏览器并获取端口"""
        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 Exception as e:
            logger.error(f"打开浏览器失败: {e}")
            return False

    def take_over_browser(self) -> bool:
        """接管浏览器"""
        if not self.tge_port:
            logger.error("浏览器端口未设置")
            return False

        try:
            co = ChromiumOptions()
            co.set_local_port(self.tge_port)
            self.page = ChromiumPage(addr_or_opts=co)
            self.page.set.window.max()
            logger.success("成功接管浏览器")
            return True
        except Exception as e:
            logger.error(f"接管浏览器失败: {e}")
            return False

    def close_extra_tabs(self) -> bool:
        """关闭多余的标签页，只保留第一个"""
        if not self.page:
            return False

        try:
            tabs = self.page.get_tabs()
            closed_count = 0
            for idx, tab in enumerate(tabs):
                if idx == 0:
                    continue
                tab.close()
                closed_count += 1
            if closed_count > 0:
                logger.info(f"已关闭{closed_count}个多余标签页")
            return True
        except Exception as e:
            logger.warning(f"关闭多余标签页失败: {e}")
            return False


# ================================================================
# 📊 浏览器交易执行器
# ================================================================

class BrowserTradingExecutor:
    """浏览器交易执行器：通过浏览器自动化下单（获取高返佣）"""

    def __init__(self, page: ChromiumPage):
        self.page = page

    def click_safe(self, xpath: str, sleep: float = 0.5) -> bool:
        """安全点击"""
        try:
            ele = self.page.ele(xpath)
            if not ele:
                return False
            ele.scroll.to_see(center=True)
            time.sleep(sleep)
            ele.click()
            return True
        except Exception as e:
            logger.error(f"点击失败 {xpath}: {e}")
            return False

    def 开单(self, marketPriceLongOrder: int = 0, limitPriceShortOrder: int = 0,
             size: Optional[float] = None, price: Optional[float] = None) -> bool:

        size = 0.1
        """
        开单操作（通过浏览器自动化，获取高返佣）

        Args:
            marketPriceLongOrder: 市价最多或者做空，1是最多，-1是做空
            limitPriceShortOrder: 限价最多或者做空，1是最多，-1是做空
            size: 数量
            price: 价格（限价单需要）

        Returns:
            是否成功
        """
        try:
            # 市价单
            if marketPriceLongOrder == -1:
                # 市价做空
                if not self.click_safe('x://button[normalize-space(text()) ="市价"]'):
                    return False
                self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
                if not self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]'):
                    return False
                logger.success(f"市价做空成功: {size}")
                return True

            elif marketPriceLongOrder == 1:
                # 市价做多
                if not self.click_safe('x://button[normalize-space(text()) ="市价"]'):
                    return False
                self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
                if not self.click_safe('x://span[normalize-space(text()) ="买入/做多"]'):
                    return False
                logger.success(f"市价做多成功: {size}")
                return True

            # 限价单
            if limitPriceShortOrder == -1:
                # 限价做空
                if not self.click_safe('x://button[normalize-space(text()) ="限价"]'):
                    return False
                self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
                time.sleep(1)
                self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
                if not self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]'):
                    return False
                logger.success(f"限价做空成功: {size} @ {price}")
                return True

            elif limitPriceShortOrder == 1:
                # 限价做多
                if not self.click_safe('x://button[normalize-space(text()) ="限价"]'):
                    return False
                self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
                time.sleep(1)
                self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
                if not self.click_safe('x://span[normalize-space(text()) ="买入/做多"]'):
                    return False
                logger.success(f"限价做多成功: {size} @ {price}")
                return True

            return False
        except Exception as e:
            logger.error(f"开单异常: {e}")
            return False

    def 平仓(self) -> bool:
        """市价平仓"""
        try:
            if self.click_safe('x://span[normalize-space(text()) ="市价"]'):
                logger.success("平仓成功")
                return True
            return False
        except Exception as e:
            logger.error(f"平仓异常: {e}")
            return False

    def place_limit_order(self, side: str, price: float, size: float) -> bool:
        """
        下限价单（通过浏览器）

        Args:
            side: "buy" 或 "sell"
            price: 价格
            size: 数量（张数）

        Returns:
            是否成功
        """
        try:
            # size已经是张数，直接使用
            if side == "buy":
                # 限价做多
                return self.开单(limitPriceShortOrder=1, size=size, price=price)
            else:
                # 限价做空
                return self.开单(limitPriceShortOrder=-1, size=size, price=price)
        except Exception as e:
            logger.error(f"限价下单异常: {e}")
            return False


# ================================================================
# 📊 BitMart API 封装（仅用于查询，不下单）
# ================================================================

class BitMartMarketMakerAPI:
    """BitMart做市API封装（仅用于查询，不下单）"""

    def __init__(self, config: MarketMakingConfig):
        self.config = config
        self.contractAPI = APIContract(
            config.api_key,
            config.secret_key,
            config.memo,
            timeout=(5, 15)
        )

    def get_order_book(self, depth: int = 20) -> Optional[OrderBook]:
        """
        获取订单簿

        Args:
            depth: 深度数量（可能不使用）

        Returns:
            OrderBook对象或None
        """
        try:
            # BitMart合约API获取深度数据
            # 根据错误信息，get_depth()不接受size参数
            # 尝试不同的调用方式
            try:
                # 方法1：不传深度参数，使用默认值（最可能的方式）
                response = self.contractAPI.get_depth(
                    contract_symbol=self.config.contract_symbol
                )[0]
            except TypeError as e1:
                try:
                    # 方法2：尝试使用 limit 参数
                    response = self.contractAPI.get_depth(
                        contract_symbol=self.config.contract_symbol,
                        limit=depth
                    )[0]
                except TypeError as e2:
                    try:
                        # 方法3：尝试使用 depth 参数
                        response = self.contractAPI.get_depth(
                            contract_symbol=self.config.contract_symbol,
                            depth=depth
                        )[0]
                    except TypeError as e3:
                        logger.error(f"get_depth()方法调用失败，尝试的参数方式都失败: {e1}, {e2}, {e3}")
                        return None

            if response.get('code') == 1000:
                data = response.get('data', {})
                # BitMart返回格式可能是不同的，需要根据实际调整
                bids = []
                asks = []

                if isinstance(data, dict):
                    bids_raw = data.get('bids', [])
                    asks_raw = data.get('asks', [])

                    # 处理不同格式
                    for b in bids_raw:
                        if isinstance(b, (list, tuple)) and len(b) >= 2:
                            bids.append((float(b[0]), float(b[1])))
                        elif isinstance(b, dict):
                            bids.append((float(b.get('price', 0)), float(b.get('size', 0))))

                    for a in asks_raw:
                        if isinstance(a, (list, tuple)) and len(a) >= 2:
                            asks.append((float(a[0]), float(a[1])))
                        elif isinstance(a, dict):
                            asks.append((float(a.get('price', 0)), float(a.get('size', 0))))

                # 买盘按价格从高到低排序，卖盘按价格从低到高排序
                bids.sort(key=lambda x: x[0], reverse=True)
                asks.sort(key=lambda x: x[0])

                if bids and asks:
                    return OrderBook(
                        bids=bids,
                        asks=asks,
                        timestamp=time.time()
                    )
            return None
        except Exception as e:
            logger.error(f"获取订单簿异常: {e}")
            # 如果获取订单簿失败，尝试使用最新价格作为备用方案
            logger.warning("尝试使用最新价格作为备用方案")
            current_price = self.get_current_price()
            if current_price:
                # 使用当前价格和价差百分比计算买一卖一
                spread_amount = current_price * self.config.spread_percent / 100
                bids = [(current_price - spread_amount / 2, 1.0)]
                asks = [(current_price + spread_amount / 2, 1.0)]
                return OrderBook(
                    bids=bids,
                    asks=asks,
                    timestamp=time.time()
                )
            return None

    def get_current_price(self) -> Optional[float]:
        """获取当前最新价格"""
        try:
            end_time = int(time.time())
            response = self.contractAPI.get_kline(
                contract_symbol=self.config.contract_symbol,
                step=1,  # 1分钟
                start_time=end_time - 60,
                end_time=end_time
            )[0]

            if response.get('code') == 1000:
                data = response.get('data', [])
                if data:
                    return float(data[-1]["close_price"])
            return None
        except Exception as e:
            logger.error(f"获取价格异常: {e}")
            return None

    def get_available_balance(self) -> Optional[float]:
        """获取合约账户可用USDT余额"""
        try:
            response = self.contractAPI.get_assets_detail()[0]
            if response.get('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(self) -> Optional[Dict]:
        """获取当前持仓"""
        try:
            response = self.contractAPI.get_position(
                contract_symbol=self.config.contract_symbol
            )[0]

            if response.get('code') == 1000:
                positions = response.get('data', [])
                if positions:
                    return positions[0]
                return None
            return None
        except Exception as e:
            logger.error(f"持仓查询异常: {e}")
            return None

    def set_leverage(self) -> bool:
        """设置杠杆和全仓模式"""
        try:
            response = self.contractAPI.post_submit_leverage(
                contract_symbol=self.config.contract_symbol,
                leverage=self.config.leverage,
                open_type=self.config.open_type
            )[0]

            if response.get('code') == 1000:
                logger.success(f"全仓模式 + {self.config.leverage}x 杠杆设置成功")
                return True
            else:
                logger.error(f"杠杆设置失败: {response}")
                return False
        except Exception as e:
            logger.error(f"设置杠杆异常: {e}")
            return False

    # ============== 新增：撤单、平仓 ==============
    def get_open_orders(self) -> List[Dict]:
        """获取当前所有挂单"""
        try:
            resp = self.contractAPI.get_open_order(
                contract_symbol=self.config.contract_symbol
            )[0]
            if resp.get("code") == 1000:
                data = resp.get("data", [])
                return data if isinstance(data, list) else []
            return []
        except Exception as e:
            logger.error(f"查询挂单异常: {e}")
            return []

    def cancel_order(self, order_id: str) -> bool:
        """撤销单个挂单"""
        try:
            resp = self.contractAPI.post_cancel_order(
                contract_symbol=self.config.contract_symbol,
                order_id=order_id
            )[0]
            if resp.get("code") == 1000:
                logger.success(f"撤单成功: {order_id}")
                return True
            logger.error(f"撤单失败: {resp}")
            return False
        except Exception as e:
            logger.error(f"撤单异常: {e}")
            return False

    def cancel_all_orders(self) -> None:
        """撤销所有挂单（无精确超时信息时，直接全撤）"""
        open_orders = self.get_open_orders()
        for od in open_orders:
            oid = str(od.get("order_id") or od.get("id") or "")
            if oid:
                self.cancel_order(oid)

    def close_position(self) -> bool:
        """
        使用API平仓（市价/近似市价）
        逻辑：查询当前持仓，根据方向下相反方向的平仓单
        """
        try:
            position = self.get_position()
            if not position:
                logger.info("无持仓，无需平仓")
                return True

            position_type = int(position.get("position_type", 0))  # 1=多, 2=空
            current_amount = float(position.get("current_amount", 0))
            if current_amount <= 0:
                logger.info("持仓数量为0，无需平仓")
                return True

            # 获取现价作为平仓价格参考
            current_price = self.get_current_price()
            if not current_price:
                logger.error("无法获取现价，平仓失败")
                return False

            # BitMart合约订单类型：3=平多（限价），4=平空（限价）
            if position_type == 1:
                order_type = 3  # 平多
            elif position_type == 2:
                order_type = 4  # 平空
            else:
                logger.error(f"未知持仓方向: {position_type}")
                return False

            # 下平仓单
            resp = self.contractAPI.post_submit_order(
                contract_symbol=self.config.contract_symbol,
                type=order_type,
                price=str(current_price),
                size=str(current_amount)
            )[0]

            if resp.get("code") == 1000:
                logger.success(f"API平仓成功，方向={position_type}, 数量={current_amount}, 价格={current_price}")
                return True

            logger.error(f"API平仓失败: {resp}")
            return False
        except Exception as e:
            logger.error(f"API平仓异常: {e}")
            return False


# ================================================================
# 📊 做市策略核心
# ================================================================

class MarketMakingStrategy:
    """被动做市策略（使用浏览器自动化下单，获取高返佣）"""

    def __init__(self, config: MarketMakingConfig, bit_id=None):
        self.bit_id = bit_id
        self.config = config
        self.api = BitMartMarketMakerAPI(config)  # 仅用于查询

        # 浏览器管理
        self.browser_manager = BrowserManager(config)
        self.trading_executor: Optional[BrowserTradingExecutor] = None

        # 订单管理（使用时间戳作为订单ID，因为浏览器下单无法直接获取订单ID）
        self.pending_orders: Dict[str, PendingOrder] = {}
        self.order_lock = Lock()

        # 统计
        self.daily_trades = 0
        self.daily_profit = 0.0
        self.total_trades = 0
        self.total_profit = 0.0

        # 运行状态
        self.running = False
        self.last_order_refresh = 0.0

        # 初始化浏览器和杠杆
        if not self._initialize_browser():
            raise Exception("浏览器初始化失败")
        self.api.set_leverage()

    def _initialize_browser(self) -> bool:
        """初始化浏览器"""
        try:
            # 打开浏览器
            if not self.browser_manager.open_browser():
                logger.error("打开浏览器失败")
                return False

            # # 接管浏览器
            # if not self.browser_manager.take_over_browser():
            #     logger.error("接管浏览器失败")
            #     return False

            # 关闭多余标签页
            self.browser_manager.close_extra_tabs()

            # 打开交易页面
            self.browser_manager.page.get(self.config.trading_url)
            time.sleep(2)  # 等待页面加载

            # 初始化交易执行器
            self.trading_executor = BrowserTradingExecutor(self.browser_manager.page)

            logger.success("浏览器初始化完成")
            return True
        except Exception as e:
            logger.error(f"浏览器初始化异常: {e}")
            return False

    def calculate_order_prices(self, order_book: OrderBook) -> Tuple[Optional[float], Optional[float]]:
        """
        计算挂单价格

        Args:
            order_book: 订单簿

        Returns:
            (buy_price, sell_price)
        """
        if not order_book.mid_price or not order_book.best_bid or not order_book.best_ask:
            return None, None

        mid = order_book.mid_price
        spread_amount = mid * self.config.spread_percent / 100

        # 买单价格：中间价 - 价差的一半，但不能低于买一
        buy_price = mid - spread_amount / 2
        buy_price = min(buy_price, order_book.best_bid * 0.9999)  # 略低于买一，确保能成交

        # 卖单价格：中间价 + 价差的一半，但不能高于卖一
        sell_price = mid + spread_amount / 2
        sell_price = max(sell_price, order_book.best_ask * 1.0001)  # 略高于卖一，确保能成交

        # 确保价差合理
        if sell_price <= buy_price:
            # 如果价差太小，使用买一卖一价格
            buy_price = order_book.best_bid * 0.9999
            sell_price = order_book.best_ask * 1.0001

        return buy_price, sell_price

    def should_refresh_orders(self) -> bool:
        """判断是否需要刷新订单"""
        now = time.time()
        if now - self.last_order_refresh >= self.config.order_refresh_interval:
            return True
        return False

    def cancel_stale_orders(self):
        """撤销超时订单（使用API撤单）"""
        now = time.time()
        to_cancel = []

        with self.order_lock:
            for order_id, order in self.pending_orders.items():
                if order.status == "pending":
                    if now - order.create_time > self.config.order_timeout:
                        to_cancel.append(order_id)

        if not to_cancel:
            return

        logger.info(f"发现{len(to_cancel)}个超时订单，尝试API撤单")
        try:
            # 先通过API获取真实挂单列表并撤单
            self.api.cancel_all_orders()
            # 本地状态同步
            with self.order_lock:
                for order_id in to_cancel:
                    if order_id in self.pending_orders:
                        self.pending_orders[order_id].status = "cancelled"
        except Exception as e:
            logger.error(f"API撤单失败: {e}")

    def update_pending_orders(self):
        """更新挂单状态（通过持仓变化判断订单是否成交）"""
        try:
            # 获取当前持仓
            current_position = self.api.get_position()
            current_position_type = 0
            current_position_amount = 0.0

            if current_position:
                current_position_type = int(current_position.get('position_type', 0))
                current_position_amount = abs(float(current_position.get('current_amount', 0)))

            with self.order_lock:
                # 检查挂单是否成交（通过持仓变化判断）
                for order_id, order in list(self.pending_orders.items()):
                    if order.status == "pending":
                        # 检查订单是否超时
                        if time.time() - order.create_time > self.config.order_timeout:
                            # 订单超时，标记为取消
                            order.status = "cancelled"
                            logger.info(f"订单超时: {order_id} {order.side} @ {order.price}")
                            continue
        except Exception as e:
            logger.error(f"更新挂单状态异常: {e}")

    def _place_counter_order(self, filled_order: PendingOrder):
        """
        订单成交后，在另一侧挂单

        Args:
            filled_order: 已成交的订单
        """
        # 等待一小段时间，确保订单状态更新
        time.sleep(0.1)

        order_book = self.api.get_order_book()
        if not order_book:
            logger.warning("无法获取订单簿，无法挂反向单")
            return

        # 计算反向订单价格
        buy_price, sell_price = self.calculate_order_prices(order_book)

        if filled_order.side == "buy":
            # 买单成交，挂卖单
            if sell_price and self.trading_executor:
                contract_size = self.config.order_size_usdt / sell_price / 0.01
                if contract_size < 1:
                    contract_size = 1

                if self.trading_executor.place_limit_order("sell", sell_price, contract_size):
                    order_id = f"sell_{int(time.time() * 1000)}"
                    with self.order_lock:
                        self.pending_orders[order_id] = PendingOrder(
                            order_id=order_id,
                            side="sell",
                            price=sell_price,
                            size=self.config.order_size_usdt,
                            create_time=time.time(),
                            status="pending"
                        )
                    logger.info(f"买单成交后挂卖单: {sell_price}, 订单ID: {order_id}")
                else:
                    logger.warning("买单成交后挂卖单失败")
        else:
            # 卖单成交，挂买单平空或开多
            if buy_price and self.trading_executor:
                contract_size = self.config.order_size_usdt / buy_price / 0.01
                if contract_size < 1:
                    contract_size = 1

                if self.trading_executor.place_limit_order("buy", buy_price, contract_size):
                    order_id = f"buy_{int(time.time() * 1000)}"
                    with self.order_lock:
                        self.pending_orders[order_id] = PendingOrder(
                            order_id=order_id,
                            side="buy",
                            price=buy_price,
                            size=self.config.order_size_usdt,
                            create_time=time.time(),
                            status="pending"
                        )
                    logger.info(f"卖单成交后挂买单: {buy_price}, 订单ID: {order_id}")
                else:
                    logger.warning("卖单成交后挂买单失败")

    def place_market_making_orders(self):
        """放置做市订单"""
        # 获取订单簿
        order_book = self.api.get_order_book()
        if not order_book or not order_book.mid_price:
            logger.warning("无法获取订单簿")
            return

        # 检查持仓
        position = self.api.get_position()
        position_value = 0.0
        if position:
            current_price = order_book.mid_price
            position_amount = abs(float(position.get('current_amount', 0)))
            # 计算持仓价值（USDT）
            position_value = position_amount * current_price

        # 如果持仓超过限制，只挂反向单
        if position_value >= self.config.max_position_usdt:
            logger.warning(f"持仓超过限制: {position_value} USDT，只挂反向单")
            # 只挂反向单平仓
            if position:
                position_type = int(position.get('position_type', 0))
                if position_type == 1:  # 多仓
                    # 挂卖单平多
                    _, sell_price = self.calculate_order_prices(order_book)
                    if sell_price and self.trading_executor:
                        contract_size = self.config.order_size_usdt / sell_price / 0.01
                        if contract_size < 1:
                            contract_size = 1
                        self.trading_executor.place_limit_order("sell", sell_price, contract_size)
                elif position_type == 2:  # 空仓
                    # 挂买单平空
                    buy_price, _ = self.calculate_order_prices(order_book)
                    if buy_price and self.trading_executor:
                        contract_size = self.config.order_size_usdt / buy_price / 0.01
                        if contract_size < 1:
                            contract_size = 1
                        self.trading_executor.place_limit_order("buy", buy_price, contract_size)
            return

        # 计算挂单价格
        buy_price, sell_price = self.calculate_order_prices(order_book)

        if not buy_price or not sell_price:
            return

        # 检查当前挂单数量
        with self.order_lock:
            pending_buy_count = sum(1 for o in self.pending_orders.values()
                                    if o.side == "buy" and o.status == "pending")
            pending_sell_count = sum(1 for o in self.pending_orders.values()
                                     if o.side == "sell" and o.status == "pending")

        # 如果两侧都有挂单，不重复挂
        if pending_buy_count > 0 and pending_sell_count > 0:
            return

        # 挂买单（通过浏览器）
        if pending_buy_count == 0:
            # 计算张数（根据合约规格调整）
            # 假设页面输入框单位是张数，需要将USDT金额转换为张数
            # size_usdt / price = ETH数量，再除以合约面值得到张数
            contract_size = self.config.order_size_usdt / buy_price / 0.01
            if contract_size < 1:
                contract_size = 1

            if self.trading_executor and self.trading_executor.place_limit_order("buy", buy_price, contract_size):
                # 使用时间戳作为订单ID
                order_id = f"buy_{int(time.time() * 1000)}"
                with self.order_lock:
                    self.pending_orders[order_id] = PendingOrder(
                        order_id=order_id,
                        side="buy",
                        price=buy_price,
                        size=self.config.order_size_usdt,
                        create_time=time.time(),
                        status="pending"
                    )
                logger.info(f"挂买单成功: {buy_price}, 订单ID: {order_id}")
            else:
                logger.warning("挂买单失败")

        # 挂卖单（通过浏览器）
        if pending_sell_count == 0:
            # 计算张数
            contract_size = self.config.order_size_usdt / sell_price / 0.01
            if contract_size < 1:
                contract_size = 1

            if self.trading_executor and self.trading_executor.place_limit_order("sell", sell_price, contract_size):
                # 使用时间戳作为订单ID
                order_id = f"sell_{int(time.time() * 1000)}"
                with self.order_lock:
                    self.pending_orders[order_id] = PendingOrder(
                        order_id=order_id,
                        side="sell",
                        price=sell_price,
                        size=self.config.order_size_usdt,
                        create_time=time.time(),
                        status="pending"
                    )
                logger.info(f"挂卖单成功: {sell_price}, 订单ID: {order_id}")
            else:
                logger.warning("挂卖单失败")

        self.last_order_refresh = time.time()

    def check_risk_limits(self) -> bool:
        """检查风险限制"""
        # 检查每日交易次数
        if self.daily_trades >= self.config.max_daily_trades:
            logger.warning(f"达到每日最大交易次数: {self.daily_trades}")
            return False

        # 检查每日亏损
        if self.daily_profit <= -self.config.max_daily_loss:
            logger.error(f"达到每日最大亏损: {self.daily_profit}")
            # send_dingtalk_message(f"做市策略达到每日最大亏损: {self.daily_profit} USDT", error=True)
            return False

        return True

    def run(self):
        """主运行循环"""
        self.running = True
        logger.info("做市策略启动")

        while self.running:
            try:
                # 检查风险限制
                if not self.check_risk_limits():
                    logger.error("风险限制触发，停止策略")
                    break

                # 撤销超时订单
                self.cancel_stale_orders()

                # 更新挂单状态
                self.update_pending_orders()

                # 刷新订单
                if self.should_refresh_orders():
                    self.place_market_making_orders()

                # 短暂休眠
                time.sleep(0.5)

            except KeyboardInterrupt:
                logger.info("收到中断信号，停止策略")
                break
            except Exception as e:
                logger.error(f"策略运行异常: {e}")
                time.sleep(1)

        # 清理：刷新页面，手动撤销挂单
        logger.info("清理挂单...使用API撤单")
        try:
            self.api.cancel_all_orders()
            with self.order_lock:
                for order_id in list(self.pending_orders.keys()):
                    if self.pending_orders[order_id].status == "pending":
                        self.pending_orders[order_id].status = "cancelled"
        except Exception as e:
            logger.error(f"清理挂单失败: {e}")

        logger.info("做市策略已停止")

    def stop(self):
        """停止策略"""
        self.running = False


# ================================================================
# 🚀 主程序
# ================================================================

if __name__ == '__main__':
    config = MarketMakingConfig(
        contract_symbol="ETHUSDT",
        bit_id="f2320f57e24c45529a009e1541e25961",  # TGE浏览器ID
        trading_url="https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT",
        spread_percent=0.01,  # 0.01%价差
        order_size_usdt=0.1,  # 每单10 USDT
        max_position_usdt=3.0,  # 最大持仓100 USDT
        order_refresh_interval=2.0,  # 2秒刷新一次
        order_timeout=60.0,  # 60秒超时
        max_daily_loss=50.0,  # 每日最大亏损50 USDT
        max_daily_trades=1000,  # 每日最大1000笔
        leverage="35",
        open_type="cross"
    )

    strategy = MarketMakingStrategy(config)

    try:
        strategy.run()
    except Exception as e:
        logger.error(f"程序异常: {e}")
        # send_dingtalk_message(f"做市策略异常: {e}", error=True)

# 9359,53
# 14.35
