This commit is contained in:
27942
2025-12-25 14:09:57 +08:00
parent 464910dc5d
commit b32fca08ab
3 changed files with 24 additions and 244 deletions

View File

@@ -3,12 +3,9 @@ import time
import uuid
import datetime
from dataclasses import dataclass
from typing import Optional
import requests
from tqdm import tqdm
from loguru import logger
from DrissionPage import ChromiumPage, ChromiumOptions
from bitmart.api_contract import APIContract
from bitmart.lib.cloud_exceptions import APIException
@@ -27,12 +24,6 @@ class StrategyConfig:
open_type: str = "cross"
leverage: str = "30"
# ===== 浏览器配置 =====
tge_id: int = 196495 # TGE浏览器ID
tge_url: str = "http://127.0.0.1:50326"
tge_authorization: str = "Bearer asp_174003986c9b0799677c5b2c1adb76e402735d753bc91a91"
trading_url: str = "https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT"
# ===== K线与指标 =====
step_min: int = 1
lookback_min: int = 240
@@ -158,115 +149,10 @@ class BitmartFuturesMeanReversionBot:
self.post_sl_ts = 0.0
self.post_sl_vol_scale = 1.0 # 记录止损时的 vol_scale
# ✅ 浏览器自动化相关
self.tge_port: Optional[int] = None
self.page: Optional[ChromiumPage] = None
self.browser_initialized = False
self.pbar = tqdm(total=60, desc="运行中(秒)", ncols=90)
logger.info(f"初始化完成,基准波动率默认值: {self._base_ratio_cached * 100:.4f}%")
# ----------------- 浏览器自动化相关 -----------------
def open_browser(self) -> bool:
"""打开 TGE 对应浏览器实例"""
try:
tge_headers = {
"Authorization": self.cfg.tge_authorization,
"Content-Type": "application/json"
}
res = requests.post(
f"{self.cfg.tge_url}/api/browser/start",
json={"envId": self.cfg.tge_id},
headers=tge_headers,
timeout=10
)
self.tge_port = res.json()["data"]["port"]
logger.success(f"成功打开浏览器,端口:{self.tge_port}")
return True
except Exception as e:
logger.error(f"打开浏览器失败: {e}")
self.ding(f"打开浏览器失败: {e}", error=True)
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}")
self.ding(f"接管浏览器失败: {e}", error=True)
return False
def close_extra_tabs(self) -> bool:
"""关闭多余 tab"""
if not self.page:
return False
try:
for idx, tab in enumerate(self.page.get_tabs()):
if idx > 0:
tab.close()
return True
except Exception as e:
logger.warning(f"关闭多余标签页失败: {e}")
return False
def initialize_browser(self) -> bool:
"""初始化浏览器(打开、接管、导航到交易页面)"""
if self.browser_initialized:
return True
# 打开浏览器
if not self.open_browser():
return False
# 接管浏览器
if not self.take_over_browser():
return False
# 关闭多余标签页
self.close_extra_tabs()
# 导航到交易页面
try:
self.page.get(self.cfg.trading_url)
time.sleep(2) # 等待页面加载
logger.success("已导航到交易页面")
except Exception as e:
logger.error(f"导航到交易页面失败: {e}")
self.ding(f"导航到交易页面失败: {e}", error=True)
return False
self.browser_initialized = True
return True
def click_safe(self, xpath: str, sleep: float = 0.5) -> bool:
"""安全点击"""
if not self.page:
return False
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 ding(self, msg, error=False):
prefix = "❌bitmart" if error else "🔔bitmart"
@@ -638,7 +524,7 @@ class BitmartFuturesMeanReversionBot:
self.trading_enabled = False
self.ding(f"达到日盈利封顶:{pnl * 100:.2f}% -> 停机")
# ----------------- 下单(使用浏览器自动化) -----------------
# ----------------- 下单 -----------------
def calculate_size(self, price: float) -> int:
bal = self.get_assets_available()
if bal < 10:
@@ -654,116 +540,6 @@ class BitmartFuturesMeanReversionBot:
return size
def place_market_order(self, side: int, size: int) -> bool:
"""
使用浏览器自动化开单(市价单)
Args:
side: 1=开多, 4=开空
size: 数量(张数)
Returns:
是否成功
"""
if size <= 0:
return False
# 确保浏览器已初始化
if not self.browser_initialized:
if not self.initialize_browser():
logger.error("浏览器初始化失败,无法开单")
self.ding("浏览器初始化失败,无法开单", error=True)
return False
try:
# 刷新页面确保在交易页面
if self.page:
self.page.get(self.cfg.trading_url)
time.sleep(1)
# 根据side判断方向
if side == 1:
# 开多:点击市价,输入数量,点击买入/做多
if not self.click_safe('x://button[normalize-space(text()) ="市价"]'):
logger.error("点击市价按钮失败")
return False
# 输入数量
try:
size_input = self.page.ele('x://*[@id="size_0"]')
if size_input:
size_input.input(str(size))
time.sleep(0.5)
else:
logger.error("找不到数量输入框")
return False
except Exception as e:
logger.error(f"输入数量失败: {e}")
return False
# 点击买入/做多
if not self.click_safe('x://span[normalize-space(text()) ="买入/做多"]'):
logger.error("点击买入/做多按钮失败")
return False
logger.success(f"浏览器自动化开多成功: size={size}")
return True
elif side == 4:
# 开空:点击市价,输入数量,点击卖出/做空
if not self.click_safe('x://button[normalize-space(text()) ="市价"]'):
logger.error("点击市价按钮失败")
return False
# 输入数量
try:
size_input = self.page.ele('x://*[@id="size_0"]')
if size_input:
size_input.input(str(size))
time.sleep(0.5)
else:
logger.error("找不到数量输入框")
return False
except Exception as e:
logger.error(f"输入数量失败: {e}")
return False
# 点击卖出/做空
if not self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]'):
logger.error("点击卖出/做空按钮失败")
return False
logger.success(f"浏览器自动化开空成功: size={size}")
return True
else:
logger.error(f"不支持的side参数: {side} (只支持1=开多, 4=开空)")
return False
except Exception as e:
logger.error(f"浏览器自动化开单异常: {e}")
self.ding(f"浏览器自动化开单异常: {e}", error=True)
return False
def close_position_all(self):
"""
使用API平仓保持原有逻辑
"""
if self.pos == 1:
ok = self.place_market_order_api(3, 999999)
if ok:
self.pos = 0
elif self.pos == -1:
ok = self.place_market_order_api(2, 999999)
if ok:
self.pos = 0
def place_market_order_api(self, side: int, size: int) -> bool:
"""
使用API下单仅用于平仓
Args:
side: 2=平空, 3=平多
size: 数量
"""
if size <= 0:
return False
@@ -780,24 +556,34 @@ class BitmartFuturesMeanReversionBot:
size=size
)[0]
logger.info(f"API平仓响应: {resp}")
logger.info(f"order_resp: {resp}")
if resp.get("code") == 1000:
return True
self.ding(f"API平仓失败: {resp}", error=True)
self.ding(f"下单失败: {resp}", error=True)
return False
except APIException as e:
logger.error(f"API平仓异常: {e}")
self.ding(f"API平仓异常: {e}", error=True)
logger.error(f"API下单异常: {e}")
self.ding(f"API下单异常: {e}", error=True)
return False
except Exception as e:
logger.error(f"API平仓未知异常: {e}")
self.ding(f"API平仓未知异常: {e}", error=True)
logger.error(f"下单未知异常: {e}")
self.ding(f"下单未知异常: {e}", error=True)
return False
def close_position_all(self):
if self.pos == 1:
ok = self.place_market_order(3, 999999)
if ok:
self.pos = 0
elif self.pos == -1:
ok = self.place_market_order(2, 999999)
if ok:
self.pos = 0
# ----------------- 止损后机制 -----------------
def _reentry_penalty_active(self, dev: float, entry_dev: float) -> bool:
"""检查是否需要应用重新入场惩罚"""
@@ -970,13 +756,6 @@ class BitmartFuturesMeanReversionBot:
self.ding("杠杆设置失败,停止运行", error=True)
return
# 初始化浏览器(延迟初始化,在需要开单时再初始化)
# 这里可以选择提前初始化或者在place_market_order中初始化
# 为了更好的用户体验,可以选择在这里提前初始化
logger.info("准备初始化浏览器...")
if not self.initialize_browser():
logger.warning("浏览器初始化失败,将在首次开单时重试")
while True:
now_dt = datetime.datetime.now()
self.pbar.n = now_dt.second
@@ -1084,3 +863,4 @@ if __name__ == "__main__":
bot.ding(f"❌ 策略异常退出: {e}", error=True)
raise
# 目前动态计算阀值的速度是多少

View File

@@ -204,7 +204,7 @@ class BitmartFuturesTransaction:
def 开单(self, marketPriceLongOrder=0, limitPriceShortOrder=0, size=None, price=None):
"""
marketPriceLongOrder 市价最多或者做空1是多,-1是做空
marketPriceLongOrder 市价最多或者做空1是多,-1是做空
limitPriceShortOrder 限价最多或者做空
"""

View File

@@ -585,10 +585,10 @@ Winwin benefits — dont miss out! 🚀
def 回复(self):
urls = [
"https://x.com/Websea_MY/status/1999305458566463782",
"https://x.com/Websea_MY/status/2000021069311402186",
"https://x.com/Websea_MY/status/2001119115575222779",
"https://x.com/Websea_MY/status/2001180999443734677"
"https://x.com/Websea_MY/status/2003700808186278388",
"https://x.com/Websea_MY/status/2003669526492405904",
# "https://x.com/Websea_MY/status/2001119115575222779",
# "https://x.com/Websea_MY/status/2001180999443734677"
]
tests = [
"Canal premium de Websea: Regístrese para disfrutar de descuentos del 85 % en las comisiones de los futuros de Websea. Para grandes volúmenes de registros, el porcentaje es negociable. Agentes bienvenidos a consultar. Contacto Telegram: @webseatom",
@@ -739,7 +739,7 @@ def run_work(x_token_info, xstart_info):
pass
# hub.to_do_tui() # 发推
# hub.回复() # 发推
hub.回复() # 发推
# hub.FollowTwitterAccount() # 关注