bitmart优化完成

This commit is contained in:
Administrator
2025-12-23 14:31:44 +08:00
parent 60de2d8300
commit be27616e3e
3 changed files with 184 additions and 58 deletions

View File

@@ -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)

Binary file not shown.

Binary file not shown.