From be27616e3e6400abb91399d26e24329c290c9312 Mon Sep 17 00:00:00 2001 From: Administrator Date: Tue, 23 Dec 2025 14:31:44 +0800 Subject: [PATCH] =?UTF-8?q?bitmart=E4=BC=98=E5=8C=96=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bitmart/交易.py | 242 ++++++++++++++++++++++++++--------- telegram/bot_session.session | Bin 45056 -> 45056 bytes telegram/sign.db | Bin 40960 -> 40960 bytes 3 files changed, 184 insertions(+), 58 deletions(-) diff --git a/bitmart/交易.py b/bitmart/交易.py index 495da76..6836c13 100644 --- a/bitmart/交易.py +++ b/bitmart/交易.py @@ -1,19 +1,21 @@ """ BitMart 被动做市/高频刷单策略 核心逻辑:在盘口两侧不断挂单,赚取价差+返佣 +使用浏览器自动化下单,获取高返佣 """ import time -import requests - +import datetime +from typing import Optional, Dict, List, Tuple +from dataclasses import dataclass from loguru import logger from threading import Lock -from dataclasses import dataclass -from typing import Optional, Dict, List, Tuple -from bitmart.api_contract import APIContract -from DrissionPage import ChromiumPage, ChromiumOptions +import requests -from 交易.tools import send_dingtalk_message +from DrissionPage import ChromiumPage, ChromiumOptions +from bitmart.api_contract import APIContract +from bitmart.lib.cloud_exceptions import APIException +# from 交易.tools import send_dingtalk_message # ================================================================ @@ -49,7 +51,7 @@ class MarketMakingConfig: max_daily_trades: int = 1000 # 每日最大交易次数 # 杠杆和模式 - leverage: str = "100" # 杠杆倍数 + leverage: str = "30" # 杠杆倍数 open_type: str = "cross" # 全仓模式 def __post_init__(self): @@ -99,7 +101,7 @@ class OrderBook: @dataclass class PendingOrder: - """ pending订单信息""" + """pending订单信息""" order_id: str side: str # "buy" or "sell" price: float @@ -305,7 +307,7 @@ class BrowserTradingExecutor: # ================================================================ class BitMartMarketMakerAPI: - """BitMart做市API封装""" + """BitMart做市API封装(仅用于查询,不下单)""" def __init__(self, config: MarketMakingConfig): self.config = config @@ -321,18 +323,37 @@ class BitMartMarketMakerAPI: 获取订单簿 Args: - depth: 深度数量 + depth: 深度数量(可能不使用) Returns: OrderBook对象或None """ try: # BitMart合约API获取深度数据 - # 注意:需要根据实际API方法调整,可能是 get_depth 或 get_market_depth - response = self.contractAPI.get_depth( - contract_symbol=self.config.contract_symbol, - size=depth - )[0] + # 根据错误信息,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', {}) @@ -370,6 +391,19 @@ class BitMartMarketMakerAPI: 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]: @@ -445,7 +479,94 @@ class BitMartMarketMakerAPI: logger.error(f"设置杠杆异常: {e}") return False - # 注意:下单操作已改为浏览器自动化,这里不再提供API下单方法 + # ============== 新增:撤单、平仓 ============== + def get_open_orders(self) -> List[Dict]: + """获取当前所有挂单""" + try: + resp = self.contractAPI.get_open_orders( + 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 # ================================================================ @@ -478,7 +599,8 @@ class MarketMakingStrategy: self.last_order_refresh = 0.0 # 初始化浏览器和杠杆 - self._initialize_browser() + if not self._initialize_browser(): + raise Exception("浏览器初始化失败") self.api.set_leverage() def _initialize_browser(self) -> bool: @@ -550,7 +672,7 @@ class MarketMakingStrategy: return False def cancel_stale_orders(self): - """撤销超时订单(通过浏览器刷新页面,手动撤销)""" + """撤销超时订单(使用API撤单)""" now = time.time() to_cancel = [] @@ -560,19 +682,20 @@ class MarketMakingStrategy: if now - order.create_time > self.config.order_timeout: to_cancel.append(order_id) - # 如果有超时订单,刷新页面(页面会自动显示最新挂单状态) - if to_cancel: - logger.info(f"发现{len(to_cancel)}个超时订单,刷新页面") - try: - self.browser_manager.page.refresh() - time.sleep(1) - # 注意:实际撤销操作需要在页面上手动点击,这里只是标记 - 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"刷新页面失败: {e}") + 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): """更新挂单状态(通过持仓变化判断订单是否成交)""" @@ -596,12 +719,6 @@ class MarketMakingStrategy: order.status = "cancelled" logger.info(f"订单超时: {order_id} {order.side} @ {order.price}") continue - - # 简单判断:如果持仓方向与订单方向一致,可能已成交 - # 注意:这个方法不够精确,实际应该通过API查询挂单状态 - # 但由于使用浏览器下单,无法直接获取订单ID,这里简化处理 - # 建议:定期刷新页面,通过页面上的挂单列表判断 - pass except Exception as e: logger.error(f"更新挂单状态异常: {e}") @@ -679,22 +796,32 @@ class MarketMakingStrategy: position_value = 0.0 if position: current_price = order_book.mid_price - position_value = abs(float(position.get('current_amount', 0)) * current_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") + logger.warning(f"持仓超过限制: {position_value} USDT,只挂反向单") # 只挂反向单平仓 - if position and float(position.get('position_type', 0)) == 1: # 多仓 - # 挂卖单平多 - _, sell_price = self.calculate_order_prices(order_book) - if sell_price: - self.api.place_limit_order("sell", sell_price, self.config.order_size_usdt) - elif position and float(position.get('position_type', 0)) == 2: # 空仓 - # 挂买单平空 - buy_price, _ = self.calculate_order_prices(order_book) - if buy_price: - self.api.place_limit_order("buy", buy_price, self.config.order_size_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 # 计算挂单价格 @@ -706,9 +833,9 @@ class MarketMakingStrategy: # 检查当前挂单数量 with self.order_lock: pending_buy_count = sum(1 for o in self.pending_orders.values() - if o.side == "buy" and o.status == "pending") + 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 o.side == "sell" and o.status == "pending") # 如果两侧都有挂单,不重复挂 if pending_buy_count > 0 and pending_sell_count > 0: @@ -774,7 +901,7 @@ class MarketMakingStrategy: # 检查每日亏损 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) + # send_dingtalk_message(f"做市策略达到每日最大亏损: {self.daily_profit} USDT", error=True) return False return True @@ -812,11 +939,9 @@ class MarketMakingStrategy: time.sleep(1) # 清理:刷新页面,手动撤销挂单 - logger.info("清理挂单...") + logger.info("清理挂单...使用API撤单") try: - self.browser_manager.page.refresh() - time.sleep(1) - # 注意:实际撤销操作需要在页面上手动点击 + 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": @@ -857,4 +982,5 @@ if __name__ == '__main__': strategy.run() except Exception as e: logger.error(f"程序异常: {e}") - send_dingtalk_message(f"做市策略异常: {e}", error=True) + # send_dingtalk_message(f"做市策略异常: {e}", error=True) + diff --git a/telegram/bot_session.session b/telegram/bot_session.session index 740d5a0b75bd20bc4c4e965e97e6285334d38d93..bd2a7592529f4f30392239d950d9c90ed22ba1dd 100644 GIT binary patch delta 98 zcmZp8z|`=7X@V3}mdQjJCm^{oK{;Ql*YQ?R@A(rqZU{24F)*^QFv)9AysMt+W&CgR z+#t1Ym{u@Vlua%Y+!4+nY+GLlG)4f+UD*Vmjr-{Hpj1A$OZtb C$|6w! delta 98 zcmZp8z|`=7X@V3}=A?--PC#;Ff^xo8yW_2(7T?|9I|Lcn7#LYtnB=u5-c`@^61cK? zZoUl*6YIvww~M`)J%tx;)+pJ)#UyAp*}&FtGk1NhB(o>y#Le9^E(rh?ZH`~LkPQI1 C1tOdP diff --git a/telegram/sign.db b/telegram/sign.db index 6d98dfe506c9e38e163efc14aeeb7c67e70bb842..58b083065894be32f3a64005ec8c48d0918e1374 100644 GIT binary patch delta 198 zcmZoTz|?SnX@WFk(nJ|&#-xo2x$@j8Mg~Tvx`sx&M#htG$m=i)OlDP3Vm-|l%NM)Z z&_IfBvYVb8qtoPGJv$~5>B$TAgc*%DKh#rZsN|m@;L;2ZSxso1fcBI5MVfYG4symgjgnnSFvS6UWod uh6?jJm<-bOUaPvbwWkwEuzGMdeseJvD`3$7k_^