加入一个回测,

This commit is contained in:
ddrwode
2026-03-05 15:51:31 +08:00
parent 3a6678089c
commit 6b1a707e3f
3 changed files with 797 additions and 56 deletions

View File

@@ -82,6 +82,7 @@ class BBDelayReversalTrader:
# 交易控制
self.last_trade_time = 0.0
self.last_kline_id = None
self.cooldown_seconds = 10 # 交易冷却时间
# 日志
self.log_dir = Path(__file__).resolve().parent
@@ -242,16 +243,24 @@ class BBDelayReversalTrader:
try:
logger.info(f"浏览器操作: 开{'' if direction == 'long' else ''} {usdt_amount}U")
# 使用框架的开单方法
# 1. 先点击"开仓"按钮
self.click_safe('x://button[normalize-space(text()) ="开仓"]')
time.sleep(0.5)
# 2. 点击市价
self.click_safe('x://button[normalize-space(text()) ="市价"]')
time.sleep(0.5)
# 3. 输入金额
self.page.ele('x://*[@id="size_0"]').input(vals=usdt_amount, clear=True)
time.sleep(0.5)
# 4. 点击开仓按钮
if direction == 'long':
# 市价做多
self.click_safe('x://button[normalize-space(text()) ="市价"]')
self.page.ele('x://*[@id="size_0"]').input(usdt_amount)
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
else:
# 市价做空
self.click_safe('x://button[normalize-space(text()) ="市价"]')
self.page.ele('x://*[@id="size_0"]').input(usdt_amount)
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
time.sleep(2)
@@ -265,14 +274,28 @@ class BBDelayReversalTrader:
try:
logger.info(f"浏览器操作: 平仓{int(ratio*100)}%")
# 1. 先点击"平仓"按钮
self.click_safe('x://button[normalize-space(text()) ="平仓"]')
time.sleep(0.5)
if ratio >= 0.99:
# 全平(使用框架的全平仓方法)
self.click_safe('x://span[normalize-space(text()) ="市价"]')
else:
# 平半(使用框架的平一半方法)
self.click_safe('x://button[normalize-space(text()) ="平仓"]')
self.click_safe('x://*[@id="futureTradeForm"]/div[5]/div[3]/div[3]/span[3]')
# 2. 全平:点击市价
self.click_safe('x://button[normalize-space(text()) ="市价"]')
time.sleep(0.5)
# 3. 点击平仓按钮
if self.position > 0:
# 平多仓
self.click_safe('x://span[normalize-space(text()) ="卖出/平多"]')
else:
# 平空仓
self.click_safe('x://span[normalize-space(text()) ="买入/平空"]')
else:
# 2. 平半点击50%按钮
self.click_safe('x://*[@id="futureTradeForm"]/div[5]/div[3]/div[3]/span[3]')
time.sleep(0.5)
# 3. 点击平仓按钮
if self.position > 0:
# 平一半多仓
self.click_safe('x://span[normalize-space(text()) ="卖出/平多"]')
@@ -286,6 +309,19 @@ class BBDelayReversalTrader:
logger.error(f"浏览器平仓失败: {e}")
return False
def can_trade(self) -> bool:
"""检查是否可以交易(冷却时间)"""
now = time.time()
if now - self.last_trade_time < self.cooldown_seconds:
remain = self.cooldown_seconds - (now - self.last_trade_time)
logger.debug(f"交易冷却中,剩余 {remain:.0f}s")
return False
return True
def update_trade_time(self):
"""更新最后交易时间"""
self.last_trade_time = time.time()
# ========== 延迟反转逻辑 ==========
def mark_delay_reversal(self, reverse_type: str, trigger_price: float, kline_id: int):
@@ -421,7 +457,12 @@ class BBDelayReversalTrader:
self.page.get(self.cfg.TRADE_URL)
time.sleep(2)
# 默认点击开仓按钮,准备开仓
self.click_safe('x://button[normalize-space(text()) ="开仓"]')
time.sleep(0.5)
self.click_safe('x://button[normalize-space(text()) ="市价"]')
page_start = False
# 定期刷新浏览器
@@ -528,6 +569,7 @@ class BBDelayReversalTrader:
self.mid_closed_half = False
self.clear_delay_reversal()
self.last_kline_id = kline_id
self.update_trade_time()
logger.success(f"✓ 延迟反转完成!")
else:
logger.error(f"平仓后仍有持仓: {self.position}")
@@ -538,45 +580,67 @@ class BBDelayReversalTrader:
prev_bb_lower = bb_lower
continue
# ===== 中轨平仓 =====
if self.position != 0 and touched_middle:
# ===== 中轨平仓(延迟反转期间不执行)=====
if self.position != 0 and touched_middle and self.delay_reverse_price is None:
if not self.mid_closed_half:
logger.info("触中轨平50%")
logger.info("📊 触中轨平50%")
if not self.can_trade():
continue
self.browser_close_position(0.5)
time.sleep(2)
time.sleep(3)
self.get_position_status()
self.mid_closed_half = True
self.last_kline_id = kline_id
self.update_trade_time()
logger.success(f"✓ 平半成功")
# 更新历史
kline_history.append(current_kline)
prev_bb_upper = bb_upper
prev_bb_lower = bb_lower
continue
elif self.mid_closed_half:
# 回到开仓价全平+反手
if (self.position > 0 and cur_low <= self.entry_price) or \
(self.position < 0 and cur_high >= self.entry_price):
logger.info("回到开仓价,全平+反手")
old_direction = 'long' if self.position > 0 else 'short'
new_direction = 'short' if old_direction == 'long' else 'long'
self.browser_close_position(1.0)
time.sleep(2)
self.get_position_status()
if self.position == 0:
balance = self.get_balance()
if balance:
usdt_amount = round(balance * self.cfg.MARGIN_PCT, 2)
self.browser_open_position(new_direction, usdt_amount)
time.sleep(2)
self.get_position_status()
if self.position != 0:
self.position_count = 1
self.mid_closed_half = False
self.last_kline_id = kline_id
continue
# ===== 开仓与加仓 =====
# ===== 回到开仓价全平+反手(仅在已平半的情况下)=====
if self.position != 0 and self.mid_closed_half and self.delay_reverse_price is None:
should_close = False
if self.position > 0 and cur_low <= self.entry_price:
should_close = True
elif self.position < 0 and cur_high >= self.entry_price:
should_close = True
if should_close:
logger.info(f"💰 回到开仓价 {self.entry_price:.2f},全平+反手")
if not self.can_trade():
continue
old_direction = 'long' if self.position > 0 else 'short'
new_direction = 'short' if old_direction == 'long' else 'long'
self.browser_close_position(1.0)
time.sleep(3)
self.get_position_status()
if self.position == 0:
balance = self.get_balance()
if balance:
usdt_amount = round(balance * self.cfg.MARGIN_PCT, 2)
self.browser_open_position(new_direction, usdt_amount)
time.sleep(3)
self.get_position_status()
if self.position != 0:
self.position_count = 1
self.mid_closed_half = False
self.last_kline_id = kline_id
self.update_trade_time()
logger.success(f"✓ 反手完成!")
# 更新历史
kline_history.append(current_kline)
prev_bb_upper = bb_upper
prev_bb_lower = bb_lower
continue
# ===== 开仓(空仓时)=====
if self.position == 0:
self.clear_delay_reversal()
@@ -588,57 +652,73 @@ class BBDelayReversalTrader:
usdt_amount = round(balance * self.cfg.MARGIN_PCT, 2)
if touched_upper:
logger.info(f"空仓触上轨,开空 {usdt_amount}U")
logger.info(f"🔴 空仓触上轨,开空 {usdt_amount}U")
if not self.can_trade():
continue
self.browser_open_position('short', usdt_amount)
time.sleep(2)
time.sleep(3)
self.get_position_status()
if self.position == -1:
self.position_count = 1
self.last_kline_id = kline_id
self.update_trade_time()
logger.success(f"✓ 开空成功")
elif touched_lower:
logger.info(f"空仓触下轨,开多 {usdt_amount}U")
logger.info(f"🟢 空仓触下轨,开多 {usdt_amount}U")
if not self.can_trade():
continue
self.browser_open_position('long', usdt_amount)
time.sleep(2)
time.sleep(3)
self.get_position_status()
if self.position == 1:
self.position_count = 1
self.last_kline_id = kline_id
self.update_trade_time()
logger.success(f"✓ 开多成功")
# ===== 延迟反转触发 =====
elif self.position > 0 and touched_upper:
# ===== 延迟反转触发(有持仓且未在延迟反转中)=====
elif self.position > 0 and touched_upper and self.delay_reverse_price is None:
logger.warning("⚠️ 多仓触上轨,标记延迟反转")
self.mark_delay_reversal('long_to_short', bb_upper, kline_id)
self.last_kline_id = kline_id
elif self.position < 0 and touched_lower:
elif self.position < 0 and touched_lower and self.delay_reverse_price is None:
logger.warning("⚠️ 空仓触下轨,标记延迟反转")
self.mark_delay_reversal('short_to_long', bb_lower, kline_id)
self.last_kline_id = kline_id
# ===== 加仓 =====
# ===== 加仓(仅首次开仓后,且未在延迟反转中)=====
elif self.position_count == 1 and self.delay_reverse_price is None:
balance = self.get_balance()
if balance:
usdt_amount = round(balance * self.cfg.MARGIN_PCT, 2)
if self.position > 0 and touched_lower:
logger.info(f"多仓触下轨,加仓 {usdt_amount}U")
logger.info(f" 多仓触下轨,加仓 {usdt_amount}U")
if not self.can_trade():
continue
self.browser_open_position('long', usdt_amount)
time.sleep(2)
time.sleep(3)
self.get_position_status()
if self.position == 1:
self.position_count = 2
self.last_kline_id = kline_id
self.update_trade_time()
logger.success(f"✓ 加仓成功")
elif self.position < 0 and touched_upper:
logger.info(f"空仓触上轨,加仓 {usdt_amount}U")
logger.info(f" 空仓触上轨,加仓 {usdt_amount}U")
if not self.can_trade():
continue
self.browser_open_position('short', usdt_amount)
time.sleep(2)
time.sleep(3)
self.get_position_status()
if self.position == -1:
self.position_count = 2
self.last_kline_id = kline_id
self.update_trade_time()
logger.success(f"✓ 加仓成功")
# 更新K线历史和布林带历史
kline_history.append(current_kline)