diff --git a/job_bitmart.py b/job_bitmart.py index 346be4a..6d923d1 100644 --- a/job_bitmart.py +++ b/job_bitmart.py @@ -106,6 +106,11 @@ class BBDelayReversalTrader: self.highest_since_open = None # 持有多仓以来的最高价 self.lowest_since_open = None # 持有空仓以来的最低价 + # 延迟平仓(碰轨不平,回落/反弹再平)状态 + self.touched_band_price = None # 最近一次触碰轨道的触发价格 + self.touched_prev_open = None # 发生触碰时的前一根K线开盘价 + self.current_kline_id = None # 记录当前在处理的K线 + # 加仓状态 self.pyramid_count = 0 # 当前已加仓次数 (0=仅首次开仓) @@ -206,6 +211,8 @@ class BBDelayReversalTrader: self.has_half_closed = False self.highest_since_open = None self.lowest_since_open = None + self.touched_band_price = None + self.touched_prev_open = None return True pos = positions[0] self.position = 1 if pos["position_type"] == 1 else -1 @@ -299,6 +306,8 @@ class BBDelayReversalTrader: self.has_half_closed = False self.highest_since_open = None self.lowest_since_open = None + self.touched_band_price = None + self.touched_prev_open = None # ------------------------------------------------------------------ # 仓位操作 @@ -539,11 +548,42 @@ class BBDelayReversalTrader: else: logger.error(f"止损平仓失败,当前仍有持仓({self.position})") - # 5. 极值更新与半仓/全平反手逻辑 (需求: 如果当前做多,涨破中轨后回落碰中轨平一半;再跌到开仓价平全仓反手) + # 5. 极值更新与半仓/延迟全平反手逻辑 if self.position == 1 and self.open_avg_price: if self.highest_since_open is None or current_price > self.highest_since_open: self.highest_since_open = current_price - + + # 延迟平仓逻辑: 之前触碰过上轨 + if self.touched_band_price is not None: + # 检查是否回落到 触发的上轨价格 或者 上一根K线的开盘价 + trigger_target = max(self.touched_band_price, self.touched_prev_open) if self.touched_prev_open else self.touched_band_price + if current_price <= trigger_target or cur_low <= trigger_target: + if self.can_trade(): + logger.info(f"多单延迟回落平仓: 当前价跌破触发点 {trigger_target:.2f} (上轨价: {self.touched_band_price}, 上根开: {self.touched_prev_open})") + self.browser_close_position() + time.sleep(1) + for _ in range(10): + if self.get_position_status() and self.position == 0: break + time.sleep(1) + self.reset_position_state() + self.click_safe('x://button[normalize-space(text()) ="开仓"]') + time.sleep(0.5) + self.click_safe('x://button[normalize-space(text()) ="市价"]') + order_usdt = self.calc_order_usdt() + if order_usdt > 0: + self.page.ele('x://*[@id="size_0"]').input(vals=order_usdt, clear=True) + time.sleep(0.5) + self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]') + time.sleep(3) + self.get_position_status() + self.write_trade_log("延迟全平反手(多转空)", current_price, bb_upper, bb_mid, bb_lower, f"多单碰上轨后回落至 {trigger_target:.2f}") + page_start = True + try: self.page.close() + except: pass + self.page = None + time.sleep(5) + continue + # 规则 A1: 涨过中轨 if self.highest_since_open >= bb_mid: # 从最高点回落触碰中轨,且未平过一半 @@ -594,7 +634,38 @@ class BBDelayReversalTrader: elif self.position == -1 and self.open_avg_price: if self.lowest_since_open is None or current_price < self.lowest_since_open: self.lowest_since_open = current_price - + + # 延迟平仓逻辑: 之前触碰过下轨 + if self.touched_band_price is not None: + # 检查是否反弹到 触发的下轨价格 或者 上一根K线的开盘价 + trigger_target = min(self.touched_band_price, self.touched_prev_open) if self.touched_prev_open else self.touched_band_price + if current_price >= trigger_target or cur_high >= trigger_target: + if self.can_trade(): + logger.info(f"空单延迟反弹平仓: 当前价突破触发点 {trigger_target:.2f} (下轨价: {self.touched_band_price}, 上根开: {self.touched_prev_open})") + self.browser_close_position() + time.sleep(1) + for _ in range(10): + if self.get_position_status() and self.position == 0: break + time.sleep(1) + self.reset_position_state() + self.click_safe('x://button[normalize-space(text()) ="开仓"]') + time.sleep(0.5) + self.click_safe('x://button[normalize-space(text()) ="市价"]') + order_usdt = self.calc_order_usdt() + if order_usdt > 0: + self.page.ele('x://*[@id="size_0"]').input(vals=order_usdt, clear=True) + time.sleep(0.5) + self.click_safe('x://span[normalize-space(text()) ="买入/做多"]') + time.sleep(3) + self.get_position_status() + self.write_trade_log("延迟全平反手(空转多)", current_price, bb_upper, bb_mid, bb_lower, f"空单碰下轨后反弹至 {trigger_target:.2f}") + page_start = True + try: self.page.close() + except: pass + self.page = None + time.sleep(5) + continue + # 规则 B1: 跌破过中轨 if self.lowest_since_open <= bb_mid: # 从最低点回升触碰中轨,且未平过一半 @@ -656,7 +727,7 @@ class BBDelayReversalTrader: reason = "" success = False - # ===== 触及上轨 → 开空 / 翻转为空 / 加仓空 ===== + # ===== 触及上轨 → 等待延迟平/开空/加仓空 ===== if touched_upper: if not self.can_trade(): time.sleep(self.cfg.POLL_INTERVAL) @@ -666,33 +737,16 @@ class BBDelayReversalTrader: f"BB({self.cfg.BB_PERIOD},{self.cfg.BB_STD})") if self.position == 1: - action = "翻转: 平多→开空" - # 在当前页面点市价平仓 - 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.reset_position_state() - # 平仓后在同一页面直接点卖出/做空 - logger.info("平仓完成,直接开空") - self.click_safe('x://button[normalize-space(text()) ="开仓"]') - time.sleep(0.5) - self.click_safe('x://button[normalize-space(text()) ="市价"]') - order_usdt = self.calc_order_usdt() - if order_usdt > 0: - self.page.ele('x://*[@id="size_0"]').input(vals=order_usdt, clear=True) - time.sleep(0.5) - self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]') - time.sleep(3) - if self.verify_position(-1): - success = True + # 延迟平仓逻辑: 记录触碰上轨的信息,等待回落后再平仓,不断创新高则更新上轨目标价 + prev_open = closed_klines[-1]["open"] if len(closed_klines) > 0 else cur_high + if self.touched_band_price is None or bb_upper > self.touched_band_price: + self.touched_band_price = bb_upper + self.touched_prev_open = prev_open + logger.info(f"多单已触碰上轨,记录延迟目标价: 轨={bb_upper:.2f}, 上根开={prev_open:.2f}。等待回落再平仓...") + + # 特殊保护: 当前一瞬间即使碰到立刻跌下来,也要依靠下一轮的主循环去判断,防止剧烈波动同K线反复触发 + self.current_kline_id = current_kline["id"] + elif self.position == 0: action = "开空" self.reset_position_state() @@ -727,7 +781,7 @@ class BBDelayReversalTrader: else: logger.info(f"已持空仓,加仓已达上限({self.pyramid_count}/{self.cfg.PYRAMID_MAX})") - # ===== 触及下轨 → 开多 / 翻转为多 / 加仓多 ===== + # ===== 触及下轨 → 等待延迟平/开多/加仓多 ===== elif touched_lower: if not self.can_trade(): time.sleep(self.cfg.POLL_INTERVAL) @@ -737,30 +791,15 @@ class BBDelayReversalTrader: f"BB({self.cfg.BB_PERIOD},{self.cfg.BB_STD})") if self.position == -1: - action = "翻转: 平空→开多" - 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.reset_position_state() - logger.info("平仓完成,直接开多") - self.click_safe('x://button[normalize-space(text()) ="开仓"]') - time.sleep(0.5) - self.click_safe('x://button[normalize-space(text()) ="市价"]') - order_usdt = self.calc_order_usdt() - if order_usdt > 0: - self.page.ele('x://*[@id="size_0"]').input(vals=order_usdt, clear=True) - time.sleep(0.5) - self.click_safe('x://span[normalize-space(text()) ="买入/做多"]') - time.sleep(3) - if self.verify_position(1): - success = True + # 延迟平仓逻辑: 记录触碰下轨的信息,等待反弹后再平仓,不断创新低则更新下轨目标价 + prev_open = closed_klines[-1]["open"] if len(closed_klines) > 0 else cur_low + if self.touched_band_price is None or bb_lower < self.touched_band_price: + self.touched_band_price = bb_lower + self.touched_prev_open = prev_open + logger.info(f"空单已触碰下轨,记录延迟目标价: 轨={bb_lower:.2f}, 上根开={prev_open:.2f}。等待反弹再平仓...") + + self.current_kline_id = current_kline["id"] + elif self.position == 0: action = "开多" self.reset_position_state()