haha
This commit is contained in:
377
bb_trade.py
377
bb_trade.py
@@ -1,22 +1,24 @@
|
||||
"""
|
||||
布林带均值回归策略 — 实盘交易
|
||||
BB(10, 2.5) | 5分钟K线 | ETH | 50x杠杆 | 每单权益1%
|
||||
布林带均值回归策略 — 实盘交易 (D方案: 递增加仓)
|
||||
BB(10, 2.5) | 5分钟K线 | ETH | 50x杠杆 | 递增加仓+1%/次 max=3
|
||||
|
||||
逻辑:
|
||||
- 价格触及上布林带 → 平多(如有) + 开空
|
||||
- 价格触及下布林带 → 平空(如有) + 开多
|
||||
- 始终持仓(多空翻转)
|
||||
- 价格触及上布林带 → 平多(如有) + 开空; 已持空则加仓
|
||||
- 价格触及下布林带 → 平空(如有) + 开多; 已持多则加仓
|
||||
- 始终持仓(多空翻转 + 同向加仓)
|
||||
- 加仓比例: 开仓1%, 第1次加仓2%, 第2次3%, 第3次4%, 最多加仓3次
|
||||
|
||||
使用 BitMart Futures API 进行开平仓
|
||||
使用浏览器自动化进行开平仓(有手续费返佣),API仅用于查询数据
|
||||
"""
|
||||
import time
|
||||
import uuid
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from loguru import logger
|
||||
from bitmart.api_contract import APIContract
|
||||
from bit_tools import openBrowser
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -30,6 +32,10 @@ class BBTradeConfig:
|
||||
|
||||
# 合约
|
||||
CONTRACT_SYMBOL = "ETHUSDT"
|
||||
TRADE_URL = "https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT"
|
||||
|
||||
# 浏览器
|
||||
BIT_ID = "62f9107d0c674925972084e282df55b3"
|
||||
|
||||
# 布林带参数
|
||||
BB_PERIOD = 10 # 10根5分钟K线 = 50分钟回看
|
||||
@@ -38,7 +44,11 @@ class BBTradeConfig:
|
||||
# 仓位管理
|
||||
LEVERAGE = 50 # 杠杆倍数
|
||||
OPEN_TYPE = "cross" # 全仓模式
|
||||
MARGIN_PCT = 0.01 # 每单用权益的1%作为保证金
|
||||
MARGIN_PCT = 0.01 # 首次开仓用权益的1%作为保证金
|
||||
|
||||
# 递增加仓 (D方案)
|
||||
PYRAMID_STEP = 0.01 # 每次加仓增加1%权益比例 (1%→2%→3%→4%)
|
||||
PYRAMID_MAX = 3 # 最多加仓3次 (首次开仓不算)
|
||||
|
||||
# 风控
|
||||
MAX_DAILY_LOSS = 50.0 # 日最大亏损(U),达到后停止交易
|
||||
@@ -76,11 +86,20 @@ class BBTrader:
|
||||
timeout=(5, 15)
|
||||
)
|
||||
|
||||
# 浏览器
|
||||
self.page: ChromiumPage | None = None
|
||||
self.page_start = True # 需要(重新)打开浏览器
|
||||
self.last_page_open_time = 0.0 # 上次打开浏览器的时间
|
||||
self.PAGE_REFRESH_INTERVAL = 180 # 每3分钟关闭重开浏览器
|
||||
|
||||
# 持仓状态: -1=空, 0=无, 1=多
|
||||
self.position = 0
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
|
||||
# 加仓状态
|
||||
self.pyramid_count = 0 # 当前已加仓次数 (0=仅首次开仓)
|
||||
|
||||
# 风控
|
||||
self.daily_pnl = 0.0
|
||||
self.daily_stopped = False
|
||||
@@ -205,133 +224,77 @@ class BBTrader:
|
||||
logger.error(f"设置杠杆异常: {e}")
|
||||
return False
|
||||
|
||||
def _gen_client_order_id(self) -> str:
|
||||
return f"BB_{uuid.uuid4().hex[:12]}"
|
||||
|
||||
def submit_order(self, side: int, size: int) -> bool:
|
||||
"""
|
||||
提交市价单
|
||||
side: 1=买入开多, 2=买入平空, 3=卖出平多, 4=卖出开空
|
||||
size: 张数
|
||||
"""
|
||||
side_names = {1: "买入开多", 2: "买入平空", 3: "卖出平多", 4: "卖出开空"}
|
||||
logger.info(f"下单: {side_names.get(side, side)} {size}张")
|
||||
# ------------------------------------------------------------------
|
||||
# 浏览器自动化
|
||||
# ------------------------------------------------------------------
|
||||
def open_browser(self) -> bool:
|
||||
"""打开浏览器并进入交易页面"""
|
||||
try:
|
||||
resp = self.api.post_submit_order(
|
||||
contract_symbol=self.cfg.CONTRACT_SYMBOL,
|
||||
client_order_id=self._gen_client_order_id(),
|
||||
side=side,
|
||||
mode=1, # GTC
|
||||
type="market",
|
||||
leverage=str(self.cfg.LEVERAGE),
|
||||
open_type=self.cfg.OPEN_TYPE,
|
||||
size=size,
|
||||
)[0]
|
||||
if resp.get("code") == 1000:
|
||||
logger.success(f"下单成功: {side_names.get(side)} {size}张 resp={resp}")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"下单失败: {resp}")
|
||||
return False
|
||||
bit_port = openBrowser(id=self.cfg.BIT_ID)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
self.last_page_open_time = time.time()
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"下单异常: {e}")
|
||||
logger.error(f"打开浏览器失败: {e}")
|
||||
return False
|
||||
|
||||
def click_safe(self, xpath, sleep=0.5) -> bool:
|
||||
"""安全点击元素"""
|
||||
try:
|
||||
ele = self.page.ele(xpath)
|
||||
if not ele:
|
||||
return False
|
||||
ele.click(by_js=True)
|
||||
time.sleep(sleep)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(f"点击失败 [{xpath}]: {e}")
|
||||
return False
|
||||
|
||||
def browser_close_position(self) -> bool:
|
||||
"""浏览器点击市价全平"""
|
||||
logger.info("浏览器操作: 市价平仓")
|
||||
return self.click_safe('x://span[normalize-space(text()) ="市价"]')
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 仓位操作
|
||||
# ------------------------------------------------------------------
|
||||
def calc_order_size(self, price: float) -> int:
|
||||
def calc_order_usdt(self, is_add: bool = False) -> float:
|
||||
"""
|
||||
根据当前权益的1%计算开仓张数
|
||||
BitMart ETH合约: 1张 = 0.01 ETH
|
||||
保证金 = equity * margin_pct
|
||||
名义价值 = margin * leverage
|
||||
数量(ETH) = 名义价值 / price
|
||||
张数 = 数量 / 0.01
|
||||
计算开仓/加仓金额(U)
|
||||
首次开仓: 余额 × MARGIN_PCT (1%)
|
||||
加仓: 余额 × (MARGIN_PCT + PYRAMID_STEP × (pyramid_count+1))
|
||||
例: 开仓1%, 第1次加仓2%, 第2次加仓3%, 第3次加仓4%
|
||||
"""
|
||||
balance = self.get_balance()
|
||||
if balance is None or balance <= 0:
|
||||
logger.warning(f"余额不足或查询失败: {balance}")
|
||||
return 0
|
||||
margin = balance * self.cfg.MARGIN_PCT
|
||||
notional = margin * self.cfg.LEVERAGE
|
||||
qty_eth = notional / price
|
||||
size = max(1, int(qty_eth / 0.01)) # 1张=0.01ETH
|
||||
logger.info(f"仓位计算: 余额={balance:.2f} 保证金={margin:.2f} "
|
||||
f"名义={notional:.2f} 数量={qty_eth:.4f}ETH = {size}张")
|
||||
return size
|
||||
|
||||
def close_current_position(self) -> bool:
|
||||
"""平掉当前持仓"""
|
||||
if not self.get_position_status():
|
||||
return False
|
||||
if self.position == 0:
|
||||
logger.info("无持仓,无需平仓")
|
||||
return True
|
||||
if self.position == 1:
|
||||
# 平多: side=3
|
||||
size = int(self.current_amount)
|
||||
return self.submit_order(side=3, size=size)
|
||||
if is_add:
|
||||
pct = self.cfg.MARGIN_PCT + self.cfg.PYRAMID_STEP * (self.pyramid_count + 1)
|
||||
else:
|
||||
# 平空: side=2
|
||||
size = int(self.current_amount)
|
||||
return self.submit_order(side=2, size=size)
|
||||
pct = self.cfg.MARGIN_PCT
|
||||
order_usdt = round(balance * pct, 2)
|
||||
logger.info(f"仓位计算: 余额={balance:.2f} × {pct:.0%} = {order_usdt} U"
|
||||
f" ({'加仓#' + str(self.pyramid_count+1) if is_add else '首次开仓'})")
|
||||
return order_usdt
|
||||
|
||||
def open_long(self, price: float) -> bool:
|
||||
"""开多"""
|
||||
size = self.calc_order_size(price)
|
||||
if size <= 0:
|
||||
return False
|
||||
return self.submit_order(side=1, size=size)
|
||||
|
||||
def open_short(self, price: float) -> bool:
|
||||
"""开空"""
|
||||
size = self.calc_order_size(price)
|
||||
if size <= 0:
|
||||
return False
|
||||
return self.submit_order(side=4, size=size)
|
||||
|
||||
def flip_to_long(self, price: float) -> bool:
|
||||
"""平空 → 开多"""
|
||||
logger.info("=== 翻转为多 ===")
|
||||
if self.position == -1:
|
||||
if not self.close_current_position():
|
||||
logger.error("平空失败,放弃开多")
|
||||
return False
|
||||
time.sleep(2)
|
||||
# 确认已无仓
|
||||
for _ in range(5):
|
||||
if self.get_position_status() and self.position == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
if self.position != 0:
|
||||
logger.warning(f"平仓后仍有持仓({self.position}),放弃开多")
|
||||
return False
|
||||
return self.open_long(price)
|
||||
|
||||
def flip_to_short(self, price: float) -> bool:
|
||||
"""平多 → 开空"""
|
||||
logger.info("=== 翻转为空 ===")
|
||||
if self.position == 1:
|
||||
if not self.close_current_position():
|
||||
logger.error("平多失败,放弃开空")
|
||||
return False
|
||||
time.sleep(2)
|
||||
for _ in range(5):
|
||||
if self.get_position_status() and self.position == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
if self.position != 0:
|
||||
logger.warning(f"平仓后仍有持仓({self.position}),放弃开空")
|
||||
return False
|
||||
return self.open_short(price)
|
||||
def verify_position(self, expected: int) -> bool:
|
||||
"""验证持仓方向"""
|
||||
if self.get_position_status():
|
||||
if self.position == expected:
|
||||
return True
|
||||
logger.warning(f"持仓方向不符: 期望{expected}, 实际{self.position}")
|
||||
return False
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 风控
|
||||
# ------------------------------------------------------------------
|
||||
def check_daily_reset(self):
|
||||
"""每日重置(UTC+8 00:00 = UTC 16:00)"""
|
||||
now = datetime.utcnow()
|
||||
now = datetime.now(timezone.utc)
|
||||
# 用UTC日期做简单日切
|
||||
today = now.date()
|
||||
if self.current_date != today:
|
||||
@@ -377,14 +340,14 @@ class BBTrader:
|
||||
logger.warning(f"写入日志失败: {e}")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 主循环
|
||||
# 主循环(浏览器流程与四分之一代码一致)
|
||||
# ------------------------------------------------------------------
|
||||
def run(self):
|
||||
"""策略主循环"""
|
||||
logger.info("=" * 60)
|
||||
logger.info(f" BB策略启动: BB({self.cfg.BB_PERIOD},{self.cfg.BB_STD})")
|
||||
logger.info(f" BB策略启动(D方案): BB({self.cfg.BB_PERIOD},{self.cfg.BB_STD})")
|
||||
logger.info(f" 合约: {self.cfg.CONTRACT_SYMBOL} | {self.cfg.LEVERAGE}x {self.cfg.OPEN_TYPE}")
|
||||
logger.info(f" 每单: 权益×{self.cfg.MARGIN_PCT:.0%}")
|
||||
logger.info(f" 首次开仓: 权益×{self.cfg.MARGIN_PCT:.0%} | 递增加仓: +{self.cfg.PYRAMID_STEP:.0%}/次 | 最多{self.cfg.PYRAMID_MAX}次")
|
||||
logger.info("=" * 60)
|
||||
|
||||
# 设置杠杆
|
||||
@@ -399,9 +362,50 @@ class BBTrader:
|
||||
logger.info(f"初始持仓状态: {self.position}")
|
||||
|
||||
last_kline_id = None # 避免同一根K线重复触发
|
||||
page_start = True # 需要打开浏览器
|
||||
|
||||
while True:
|
||||
|
||||
# ===== 浏览器管理 =====
|
||||
# page_start时: 打开浏览器 → 导航 → 点市价 → 输入张数
|
||||
if page_start:
|
||||
for i in range(5):
|
||||
if self.open_browser():
|
||||
logger.info("浏览器打开成功")
|
||||
break
|
||||
else:
|
||||
logger.error("打开浏览器失败!")
|
||||
return
|
||||
|
||||
self.page.get(self.cfg.TRADE_URL)
|
||||
time.sleep(2)
|
||||
# 点击市价模式
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
time.sleep(0.5)
|
||||
|
||||
# 计算并预输入开仓金额(U)
|
||||
current_price = self.get_current_price()
|
||||
if current_price:
|
||||
order_usdt = self.calc_order_usdt()
|
||||
if order_usdt > 0:
|
||||
self.page.ele('x://*[@id="size_0"]').input(vals=order_usdt, clear=True)
|
||||
logger.info(f"预输入开仓金额: {order_usdt} U")
|
||||
|
||||
page_start = False
|
||||
|
||||
try:
|
||||
# 每3分钟关闭浏览器重新打开
|
||||
if time.time() - self.last_page_open_time >= self.PAGE_REFRESH_INTERVAL:
|
||||
logger.info("浏览器已打开超过3分钟,关闭刷新")
|
||||
try:
|
||||
self.page.close()
|
||||
except Exception:
|
||||
pass
|
||||
self.page = None
|
||||
page_start = True
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
self.check_daily_reset()
|
||||
|
||||
if self.daily_stopped:
|
||||
@@ -416,10 +420,8 @@ class BBTrader:
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
|
||||
# 当前K线 = 最后一根(未收盘),信号用已收盘的K线
|
||||
# 使用倒数第二根及之前的收盘价算BB(已收盘的K线)
|
||||
closed_klines = klines[:-1] # 已收盘的K线
|
||||
current_kline = klines[-1] # 当前未收盘K线
|
||||
closed_klines = klines[:-1]
|
||||
current_kline = klines[-1]
|
||||
|
||||
if len(closed_klines) < self.cfg.BB_PERIOD:
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
@@ -439,11 +441,11 @@ class BBTrader:
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
|
||||
# 用当前K线的 high/low 判断是否触及布林带
|
||||
cur_high = current_kline["high"]
|
||||
cur_low = current_kline["low"]
|
||||
touched_upper = cur_high >= bb_upper
|
||||
touched_lower = cur_low <= bb_lower
|
||||
# 容错: K线high/low + 当前实时价格,任一触及即算触碰
|
||||
touched_upper = cur_high >= bb_upper or current_price >= bb_upper
|
||||
touched_lower = cur_low <= bb_lower or current_price <= bb_lower
|
||||
|
||||
logger.info(
|
||||
f"价格={current_price:.2f} | "
|
||||
@@ -458,15 +460,12 @@ class BBTrader:
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
|
||||
# 5. 信号判断 + 执行
|
||||
# 同一根K线只触发一次
|
||||
# 5. 信号判断
|
||||
kline_id = current_kline["id"]
|
||||
if kline_id == last_kline_id:
|
||||
# 已在这根K线触发过,不重复操作
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
|
||||
# 同时触及上下轨(极端波动)→ 跳过
|
||||
if touched_upper and touched_lower:
|
||||
logger.warning("同时触及上下轨,跳过")
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
@@ -474,9 +473,10 @@ class BBTrader:
|
||||
|
||||
action = None
|
||||
reason = ""
|
||||
success = False
|
||||
|
||||
# ===== 触及上轨 → 开空 / 翻转为空 / 加仓空 =====
|
||||
if touched_upper:
|
||||
# 触及上轨 → 开空 / 翻转为空
|
||||
if not self.can_trade():
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
@@ -486,24 +486,52 @@ class BBTrader:
|
||||
|
||||
if self.position == 1:
|
||||
action = "翻转: 平多→开空"
|
||||
success = self.flip_to_short(current_price)
|
||||
# 在当前页面点市价平仓
|
||||
self.browser_close_position()
|
||||
time.sleep(1)
|
||||
# 等待确认平仓
|
||||
for _ in range(10):
|
||||
if self.get_position_status() and self.position == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
if self.position != 0:
|
||||
logger.warning(f"平仓后仍有持仓({self.position}),放弃开空")
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
# 翻转时重置加仓计数
|
||||
self.pyramid_count = 0
|
||||
# 平仓后在同一页面直接点卖出/做空
|
||||
logger.info("平仓完成,直接开空")
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
time.sleep(3)
|
||||
if self.verify_position(-1):
|
||||
success = True
|
||||
elif self.position == 0:
|
||||
action = "开空"
|
||||
success = self.open_short(current_price)
|
||||
self.pyramid_count = 0
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
time.sleep(3)
|
||||
if self.verify_position(-1):
|
||||
success = True
|
||||
elif self.position == -1 and self.pyramid_count < self.cfg.PYRAMID_MAX:
|
||||
# 已持空仓 + 再次触上轨 → 加仓做空
|
||||
action = f"加仓空#{self.pyramid_count+1}"
|
||||
reason += f" (加仓#{self.pyramid_count+1}/{self.cfg.PYRAMID_MAX})"
|
||||
# 重新计算加仓金额并输入
|
||||
add_usdt = self.calc_order_usdt(is_add=True)
|
||||
if add_usdt > 0:
|
||||
self.page.ele('x://*[@id="size_0"]').input(vals=add_usdt, clear=True)
|
||||
time.sleep(0.5)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
time.sleep(3)
|
||||
if self.verify_position(-1):
|
||||
self.pyramid_count += 1
|
||||
success = True
|
||||
else:
|
||||
# 已经是空仓,不操作
|
||||
logger.info("已持空仓,触上轨无需操作")
|
||||
success = False
|
||||
|
||||
if success:
|
||||
last_kline_id = kline_id
|
||||
self.last_trade_time = time.time()
|
||||
self.write_trade_log(action, current_price,
|
||||
bb_upper, bb_mid, bb_lower, reason)
|
||||
logger.success(f"{action} 执行成功")
|
||||
logger.info(f"已持空仓,加仓已达上限({self.pyramid_count}/{self.cfg.PYRAMID_MAX})")
|
||||
|
||||
# ===== 触及下轨 → 开多 / 翻转为多 / 加仓多 =====
|
||||
elif touched_lower:
|
||||
# 触及下轨 → 开多 / 翻转为多
|
||||
if not self.can_trade():
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
@@ -513,20 +541,62 @@ class BBTrader:
|
||||
|
||||
if self.position == -1:
|
||||
action = "翻转: 平空→开多"
|
||||
success = self.flip_to_long(current_price)
|
||||
self.browser_close_position()
|
||||
time.sleep(1)
|
||||
for _ in range(10):
|
||||
if self.get_position_status() and self.position == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
if self.position != 0:
|
||||
logger.warning(f"平仓后仍有持仓({self.position}),放弃开多")
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
continue
|
||||
# 翻转时重置加仓计数
|
||||
self.pyramid_count = 0
|
||||
logger.info("平仓完成,直接开多")
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
time.sleep(3)
|
||||
if self.verify_position(1):
|
||||
success = True
|
||||
elif self.position == 0:
|
||||
action = "开多"
|
||||
success = self.open_long(current_price)
|
||||
self.pyramid_count = 0
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
time.sleep(3)
|
||||
if self.verify_position(1):
|
||||
success = True
|
||||
elif self.position == 1 and self.pyramid_count < self.cfg.PYRAMID_MAX:
|
||||
# 已持多仓 + 再次触下轨 → 加仓做多
|
||||
action = f"加仓多#{self.pyramid_count+1}"
|
||||
reason += f" (加仓#{self.pyramid_count+1}/{self.cfg.PYRAMID_MAX})"
|
||||
# 重新计算加仓金额并输入
|
||||
add_usdt = self.calc_order_usdt(is_add=True)
|
||||
if add_usdt > 0:
|
||||
self.page.ele('x://*[@id="size_0"]').input(vals=add_usdt, clear=True)
|
||||
time.sleep(0.5)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
time.sleep(3)
|
||||
if self.verify_position(1):
|
||||
self.pyramid_count += 1
|
||||
success = True
|
||||
else:
|
||||
logger.info("已持多仓,触下轨无需操作")
|
||||
success = False
|
||||
logger.info(f"已持多仓,加仓已达上限({self.pyramid_count}/{self.cfg.PYRAMID_MAX})")
|
||||
|
||||
if success:
|
||||
last_kline_id = kline_id
|
||||
self.last_trade_time = time.time()
|
||||
self.write_trade_log(action, current_price,
|
||||
bb_upper, bb_mid, bb_lower, reason)
|
||||
logger.success(f"{action} 执行成功")
|
||||
# ===== 交易成功后处理 =====
|
||||
if success and action:
|
||||
last_kline_id = kline_id
|
||||
self.last_trade_time = time.time()
|
||||
self.write_trade_log(action, current_price,
|
||||
bb_upper, bb_mid, bb_lower, reason)
|
||||
logger.success(f"{action} 执行成功")
|
||||
# 交易完成后关闭浏览器,下轮重新打开
|
||||
page_start = True
|
||||
try:
|
||||
self.page.close()
|
||||
except Exception:
|
||||
pass
|
||||
self.page = None
|
||||
time.sleep(5)
|
||||
|
||||
time.sleep(self.cfg.POLL_INTERVAL)
|
||||
|
||||
@@ -535,6 +605,7 @@ class BBTrader:
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(f"主循环异常: {e}")
|
||||
page_start = True
|
||||
time.sleep(10)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user