加入一个回测,
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user