From c929b94031aa24233eb1ca76fed4013391d3bb58 Mon Sep 17 00:00:00 2001 From: ddrwode <34234@3来 34> Date: Fri, 6 Feb 2026 10:04:36 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=89=8D=E6=94=B9=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bitmart/四分之一,五分钟,反手条件充足.py | 98 +++++++++++++++-------- 1 file changed, 66 insertions(+), 32 deletions(-) diff --git a/bitmart/四分之一,五分钟,反手条件充足.py b/bitmart/四分之一,五分钟,反手条件充足.py index cf50f33..18d6a1d 100644 --- a/bitmart/四分之一,五分钟,反手条件充足.py +++ b/bitmart/四分之一,五分钟,反手条件充足.py @@ -1,4 +1,3 @@ -import random import time from tqdm import tqdm @@ -24,9 +23,8 @@ class BitmartFuturesTransaction: self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15)) self.start = 0 # 持仓状态: -1 空, 0 无, 1 多 - self.direction = None - self.pbar = tqdm(total=30, desc="等待K线", ncols=80) + self.pbar = tqdm(total=30, desc="等待K线", ncols=80) # 可选:用于长时间等待时展示进度 self.last_kline_time = None # 上一次处理的K线时间戳,用于判断是否是新K线 @@ -35,9 +33,14 @@ class BitmartFuturesTransaction: self.reverse_min_move_pct = 0.05 # 反手最小价差过滤(百分比) self.last_reverse_time = None # 上次反手时间 + # 开仓频率控制 + self.open_cooldown_seconds = 60 # 开仓冷却时间(秒),两次开仓至少间隔此时长 + self.last_open_time = None # 上次开仓时间 + self.last_open_kline_id = None # 上次开仓所在 K 线 id,同一根 K 线只允许开仓一次 + self.leverage = "100" # 高杠杆(全仓模式下可开更大仓位) self.open_type = "cross" # 全仓模式 - self.risk_percent = 0 # 每次开仓使用可用余额的 1% + self.risk_percent = 0 # 未使用;若启用则可为每次开仓占可用余额的百分比 self.take_profit_usd = 5 # 仓位盈利达到此金额(美元)时平仓止盈 self.stop_loss_usd = -3 # 固定止损:亏损达到 3 美元平仓 self.trailing_activation_usd = 2 # 盈利达到此金额后启动移动止损 @@ -48,6 +51,7 @@ class BitmartFuturesTransaction: self.current_amount = None # 持仓量 self.bit_id = bit_id + self.default_order_size = 25 # 开仓/反手张数,统一在此修改 # 策略相关变量 self.prev_kline = None # 上一根K线 @@ -62,7 +66,7 @@ class BitmartFuturesTransaction: # 获取足够多的条目确保有最新的K线 response = self.contractAPI.get_kline( contract_symbol=self.contract_symbol, - step=5, # 30分钟 + step=5, # 5分钟 start_time=end_time - 3600 * 3, # 取最近3小时 end_time=end_time )[0]["data"] @@ -370,6 +374,18 @@ class BitmartFuturesTransaction: return None + def can_open(self, current_kline_id): + """开仓前过滤:同一根 K 线只开一次 + 开仓冷却时间。仅用于 long/short 新开仓。""" + now = time.time() + if self.last_open_kline_id is not None and self.last_open_kline_id == current_kline_id: + logger.info(f"开仓频率控制:本 K 线({current_kline_id})已开过仓,跳过") + return False + if self.last_open_time is not None and now - self.last_open_time < self.open_cooldown_seconds: + remain = self.open_cooldown_seconds - (now - self.last_open_time) + logger.info(f"开仓冷却中,剩余 {remain:.0f} 秒") + return False + return True + def can_reverse(self, current_price, trigger_price): """反手前过滤:冷却时间 + 最小价差""" now = time.time() @@ -424,10 +440,10 @@ class BitmartFuturesTransaction: logger.error("查询持仓状态失败") return False - def execute_trade(self, signal, size=1): - """执行交易""" + def execute_trade(self, signal, size=None): + """执行交易。size 不传或为 None 时使用 default_order_size。""" signal_type, trigger_price = signal - size = 25 + size = self.default_order_size if size is None else size if signal_type == 'long': # 开多前先确认无持仓 @@ -446,6 +462,8 @@ class BitmartFuturesTransaction: # 验证开仓是否成功 if self.verify_position_direction(1): self.max_unrealized_pnl_seen = None # 新仓位重置移动止损记录 + self.last_open_time = time.time() + self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None) logger.success("开多成功") return True else: @@ -469,6 +487,8 @@ class BitmartFuturesTransaction: # 验证开仓是否成功 if self.verify_position_direction(-1): self.max_unrealized_pnl_seen = None # 新仓位重置移动止损记录 + self.last_open_time = time.time() + self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None) logger.success("开空成功") return True else: @@ -476,44 +496,53 @@ class BitmartFuturesTransaction: return False elif signal_type == 'reverse_long': - # 平空 + 开多(反手做多)- 优化:平仓后立即开仓 + # 平空 + 开多(反手做多):先平仓,确认无仓后再开多,避免双向持仓 logger.info(f"执行反手做多,触发价: {trigger_price:.2f}") self.平仓() - # time.sleep(1) # 等待1秒让平仓订单提交并更新UI - - # 立即执行开多,不等待平仓验证完成(市价单通常毫秒级成交) - logger.info("平仓已提交,立即执行开多") + time.sleep(1) # 给交易所处理平仓的时间 + # 轮询确认已无持仓再开多(最多等约 10 秒) + for _ in range(10): + if self.get_position_status() and self.start == 0: + break + time.sleep(1) + if self.start != 0: + logger.warning("反手做多:平仓后仍有持仓,放弃本次开多") + return False + logger.info("已确认无持仓,执行开多") self.开单(marketPriceLongOrder=1, size=size) - time.sleep(3) # 等待订单执行 + time.sleep(3) - # 验证开仓是否成功 if self.verify_position_direction(1): - self.max_unrealized_pnl_seen = None # 新仓位重置移动止损记录 + self.max_unrealized_pnl_seen = None logger.success("反手做多成功") self.last_reverse_time = time.time() - time.sleep(20) # 额外等待避免频繁交易 + time.sleep(20) return True else: logger.error("反手做多后持仓验证失败") return False elif signal_type == 'reverse_short': - # 平多 + 开空(反手做空)- 优化:平仓后立即开仓 + # 平多 + 开空(反手做空):先平仓,确认无仓后再开空 logger.info(f"执行反手做空,触发价: {trigger_price:.2f}") self.平仓() - # time.sleep(1) # 等待1秒让平仓订单提交并更新UI - - # 立即执行开空,不等待平仓验证完成(市价单通常毫秒级成交) - logger.info("平仓已提交,立即执行开空") + time.sleep(1) + for _ in range(10): + if self.get_position_status() and self.start == 0: + break + time.sleep(1) + if self.start != 0: + logger.warning("反手做空:平仓后仍有持仓,放弃本次开空") + return False + logger.info("已确认无持仓,执行开空") self.开单(marketPriceLongOrder=-1, size=size) - time.sleep(3) # 等待订单执行 + time.sleep(3) - # 验证开仓是否成功 if self.verify_position_direction(-1): - self.max_unrealized_pnl_seen = None # 新仓位重置移动止损记录 + self.max_unrealized_pnl_seen = None logger.success("反手做空成功") self.last_reverse_time = time.time() - time.sleep(20) # 额外等待避免频繁交易 + time.sleep(20) return True else: logger.error("反手做空后持仓验证失败") @@ -548,8 +577,8 @@ class BitmartFuturesTransaction: # 进入交易页面 self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT") self.click_safe('x://button[normalize-space(text()) ="市价"]') - size = 25 - self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True) + + self.page.ele('x://*[@id="size_0"]').input(vals=25, clear=True) page_start = False @@ -622,24 +651,29 @@ class BitmartFuturesTransaction: if not self.can_reverse(current_price, signal[1]): signal = None + # 5.5 开仓频率过滤:同一根 K 线只开一次 + 开仓冷却 + if signal and signal[0] in ('long', 'short'): + if not self.can_open(current_kline_time): + signal = None + else: + self._current_kline_id_for_open = current_kline_time # 供 execute_trade 成功后记录 + # 6. 有信号则执行交易 if signal: - trade_success = self.execute_trade(signal, size=1) + trade_success = self.execute_trade(signal) if trade_success: logger.success(f"交易执行完成: {signal[0]}, 当前持仓状态: {self.start}") page_start = True else: logger.warning(f"交易执行失败或被阻止: {signal[0]}") - # 6. 短暂等待后继续循环(同一根K线遇到信号就操作) + # 短暂等待后继续循环(同一根K线遇到信号就操作) time.sleep(0.1) if page_start: self.page.close() time.sleep(5) - page_start = True - except KeyboardInterrupt: logger.info("用户中断,程序退出") break