bitmart优化完成
This commit is contained in:
242
bitmart/交易.py
242
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)
|
||||
|
||||
|
||||
Binary file not shown.
BIN
telegram/sign.db
BIN
telegram/sign.db
Binary file not shown.
Reference in New Issue
Block a user