diff --git a/bitmart/四分之一,五分钟,反手条件充足修改版.py b/bitmart/四分之一,五分钟,反手条件充足修改版.py index 3a8ba2a..879eef4 100644 --- a/bitmart/四分之一,五分钟,反手条件充足修改版.py +++ b/bitmart/四分之一,五分钟,反手条件充足修改版.py @@ -1,3 +1,4 @@ +import sys import time from tqdm import tqdm @@ -8,6 +9,39 @@ from DrissionPage import ChromiumOptions from bitmart.api_contract import APIContract +# Claude 风格控制台:左侧竖线 + 仅消息(无时间戳),颜色由下方 LOG_* 控制 +logger.remove() +logger.add(sys.stderr, format="\033[2m│\033[0m {message}", colorize=False) + +# 颜色标签 + 分块边框 +_R = "\033[0m" # reset +_B = "\033[1m" # bold +_D = "\033[2m" # dim +_C = "\033[36m" # cyan - 价格/数据 +_Y = "\033[33m" # yellow - 信号 +_G = "\033[32m" # green - 仓位/操作 +_M = "\033[90m" # gray - 系统 +_W = "\033[97m" # white - 高亮 + +def _tag(t: str, color: str) -> str: + return color + "[" + t + "]" + _R + " " + +LOG_PRICE = _tag("价格", _C) +LOG_SIGNAL = _tag("信号", _Y) +LOG_POSITION = _tag("仓位", _G) +LOG_SYSTEM = _tag("系统", _M) + +def log_kline_header(kline_id): + """新 K 线分块头(Claude 风格面板)""" + s = f" K 线 {kline_id} " + width = max(52, len(s) + 4) + line = "─" * (width - 2) + pad_left = (width - 2 - len(s)) // 2 + pad_right = width - 2 - len(s) - pad_left + logger.info(_M + "╭" + line + "╮" + _R) + logger.info(_M + "│" + _R + " " * pad_left + _B + _W + s + _R + " " * pad_right + _M + "│" + _R) + logger.info(_M + "╰" + line + "╯" + _R) + class BitmartFuturesTransaction: def __init__(self, bit_id): @@ -366,7 +400,7 @@ class BitmartFuturesTransaction: # 实体过小不交易(实体 < 0.1) if prev_entity < 0.1: - logger.info(f"上一根K线实体过小: {prev_entity:.4f},跳过信号检测") + logger.info(LOG_PRICE + f"上一根K线实体过小: {prev_entity:.4f},跳过信号检测") return None # 获取上一根K线的实体上下边 @@ -409,52 +443,42 @@ class BitmartFuturesTransaction: if use_current_open_as_base: if prev_is_bullish_for_calc and current_open_above_prev_close: - logger.info(f"上一根阳线且当前开盘价({current_kline['open']:.2f})>上一根收盘价({prev_kline['close']:.2f}),以当前开盘价为基准计算") + logger.info(LOG_PRICE + f"上一根阳线且当前开盘价({current_kline['open']:.2f})>上一根收盘价({prev_kline['close']:.2f}),以当前开盘价为基准计算") else: - logger.info(f"上一根阴线且当前开盘价({current_kline['open']:.2f})<上一根收盘价({prev_kline['close']:.2f}),以当前开盘价为基准计算") - logger.info(f"当前价格: {current_price:.2f}, 上一根实体: {prev_entity:.4f}") - logger.info(f"上一根实体上边: {prev_entity_upper:.2f}, 下边: {prev_entity_lower:.2f}") - logger.info(f"做多触发价(下1/4): {long_trigger:.2f}, 做空触发价(上1/4): {short_trigger:.2f}") - logger.info(f"突破做多价(上1/4外): {long_breakout:.2f}, 突破做空价(下1/4外): {short_breakout:.2f}") + logger.info(LOG_PRICE + f"上一根阴线且当前开盘价({current_kline['open']:.2f})<上一根收盘价({prev_kline['close']:.2f}),以当前开盘价为基准计算") + logger.info(LOG_PRICE + f"当前价: {current_price:.2f} | 上一根实体: {prev_entity:.4f} | 实体上边: {prev_entity_upper:.2f} 下边: {prev_entity_lower:.2f}") + logger.info(LOG_PRICE + f"做多触发(下1/4): {long_trigger:.2f} | 做空触发(上1/4): {short_trigger:.2f} | 突破做多: {long_breakout:.2f} | 突破做空: {short_breakout:.2f}") if skip_short_by_upper_third: - logger.info("上一根阴线+当前阳线(做多形态),不按上四分之一做空") + logger.info(LOG_PRICE + "上一根阴+当前阳(做多形态),不按上1/4做空") if skip_long_by_lower_third: - logger.info("上一根阳线+当前阴线(做空形态),不按下四分之一做多") + logger.info(LOG_PRICE + "上一根阳+当前阴(做空形态),不按下1/4做多") # 无持仓时检查开仓信号 if self.start == 0: if current_price >= long_breakout and not skip_long_by_lower_third: - logger.info(f"触发做多信号!价格 {current_price:.2f} >= 突破价(上1/4外) {long_breakout:.2f}") + logger.info(LOG_SIGNAL + f"触发做多 | 价 {current_price:.2f} >= 突破价 {long_breakout:.2f}") return ('long', long_breakout) elif current_price <= short_breakout and not skip_short_by_upper_third: - logger.info(f"触发做空信号!价格 {current_price:.2f} <= 突破价(下1/4外) {short_breakout:.2f}") + logger.info(LOG_SIGNAL + f"触发做空 | 价 {current_price:.2f} <= 突破价 {short_breakout:.2f}") return ('short', short_breakout) # 持仓时检查反手信号 elif self.start == 1: # 持多仓 - # 反手条件1: 价格跌到上一根K线的上三分之一处(做空触发价);上一根阴线+当前阳线做多时跳过 if current_price <= short_trigger and not skip_short_by_upper_third: - logger.info(f"持多反手做空!价格 {current_price:.2f} <= 触发价(上1/4) {short_trigger:.2f}") + logger.info(LOG_SIGNAL + f"持多→反手做空 | 价 {current_price:.2f} <= 触发价 {short_trigger:.2f}") return ('reverse_short', short_trigger) - - # 反手条件2: 上一根K线上阴线涨幅>0.01%,当前跌到上一根实体下边 upper_shadow_pct = self.calculate_upper_shadow(prev_kline) if upper_shadow_pct > 0.01 and current_price <= prev_entity_lower: - logger.info(f"持多反手做空!上阴线涨幅 {upper_shadow_pct:.4f}% > 0.01%," - f"价格 {current_price:.2f} <= 实体下边 {prev_entity_lower:.2f}") + logger.info(LOG_SIGNAL + f"持多→反手做空 | 上阴线 {upper_shadow_pct:.4f}% 价<=实体下边 {prev_entity_lower:.2f}") return ('reverse_short', prev_entity_lower) elif self.start == -1: # 持空仓 - # 反手条件1: 价格涨到上一根K线的下三分之一处(做多触发价);上一根阳线+当前阴线做空时跳过 if current_price >= long_trigger and not skip_long_by_lower_third: - logger.info(f"持空反手做多!价格 {current_price:.2f} >= 触发价(下1/4) {long_trigger:.2f}") + logger.info(LOG_SIGNAL + f"持空→反手做多 | 价 {current_price:.2f} >= 触发价 {long_trigger:.2f}") return ('reverse_long', long_trigger) - - # 反手条件2: 上一根K线下阴线跌幅>0.01%,当前涨到上一根实体上边 lower_shadow_pct = self.calculate_lower_shadow(prev_kline) if lower_shadow_pct > 0.01 and current_price >= prev_entity_upper: - logger.info(f"持空反手做多!下阴线跌幅 {lower_shadow_pct:.4f}% > 0.01%," - f"价格 {current_price:.2f} >= 实体上边 {prev_entity_upper:.2f}") + logger.info(LOG_SIGNAL + f"持空→反手做多 | 下阴线 {lower_shadow_pct:.4f}% 价>=实体上边 {prev_entity_upper:.2f}") return ('reverse_long', prev_entity_upper) return None @@ -464,7 +488,7 @@ class BitmartFuturesTransaction: now = time.time() 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} 秒") + logger.info(LOG_SYSTEM + f"开仓冷却中,剩余 {remain:.0f} 秒") return False return True @@ -473,13 +497,13 @@ class BitmartFuturesTransaction: now = time.time() if self.last_reverse_time and now - self.last_reverse_time < self.reverse_cooldown_seconds: remain = self.reverse_cooldown_seconds - (now - self.last_reverse_time) - logger.info(f"反手冷却中,剩余 {remain:.0f} 秒") + logger.info(LOG_SYSTEM + f"反手冷却中,剩余 {remain:.0f} 秒") return False if trigger_price and trigger_price > 0: move_pct = abs(current_price - trigger_price) / trigger_price * 100 if move_pct < self.reverse_min_move_pct: - logger.info(f"反手价差不足: {move_pct:.4f}% < {self.reverse_min_move_pct}%") + logger.info(LOG_SYSTEM + f"反手价差不足: {move_pct:.4f}% < {self.reverse_min_move_pct}%") return False return True @@ -492,7 +516,7 @@ class BitmartFuturesTransaction: for i in range(max_retries): if self.get_position_status(): if self.start == 0: - logger.info(f"确认无持仓,可以开仓") + logger.info(LOG_POSITION + "确认无持仓,可以开仓") return True else: logger.warning( @@ -513,7 +537,7 @@ class BitmartFuturesTransaction: """ if self.get_position_status(): if self.start == expected_direction: - logger.info(f"持仓方向验证成功: {self.start}") + logger.info(LOG_POSITION + f"持仓方向验证成功: {self.start}") return True else: logger.warning(f"持仓方向不符: 期望 {expected_direction}, 实际 {self.start}") @@ -529,7 +553,7 @@ class BitmartFuturesTransaction: if signal_type == 'long': # 开多前先确认无持仓 - logger.info(f"准备开多,触发价: {trigger_price:.2f}") + logger.info(LOG_POSITION + f"准备开多,触发价: {trigger_price:.2f}") if not self.get_position_status(): logger.error("开仓前查询持仓状态失败,放弃开仓") return False @@ -537,7 +561,7 @@ class BitmartFuturesTransaction: logger.warning(f"开多前发现已有持仓 (方向: {self.start}),放弃开仓避免双向持仓") return False - logger.info(f"确认无持仓,执行开多") + logger.info(LOG_POSITION + "确认无持仓,执行开多") self.开单(marketPriceLongOrder=1, size=size) time.sleep(3) # 等待订单执行 @@ -546,7 +570,7 @@ class BitmartFuturesTransaction: 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("开多成功") + logger.success(LOG_POSITION + "开多成功") return True else: logger.error("开多后持仓验证失败") @@ -554,7 +578,7 @@ class BitmartFuturesTransaction: elif signal_type == 'short': # 开空前先确认无持仓 - logger.info(f"准备开空,触发价: {trigger_price:.2f}") + logger.info(LOG_POSITION + f"准备开空,触发价: {trigger_price:.2f}") if not self.get_position_status(): logger.error("开仓前查询持仓状态失败,放弃开仓") return False @@ -562,7 +586,7 @@ class BitmartFuturesTransaction: logger.warning(f"开空前发现已有持仓 (方向: {self.start}),放弃开仓避免双向持仓") return False - logger.info(f"确认无持仓,执行开空") + logger.info(LOG_POSITION + "确认无持仓,执行开空") self.开单(marketPriceLongOrder=-1, size=size) time.sleep(3) # 等待订单执行 @@ -571,7 +595,7 @@ class BitmartFuturesTransaction: 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("开空成功") + logger.success(LOG_POSITION + "开空成功") return True else: logger.error("开空后持仓验证失败") @@ -579,7 +603,7 @@ class BitmartFuturesTransaction: elif signal_type == 'reverse_long': # 平空 + 开多(反手做多):先平仓,确认无仓后再开多,避免双向持仓 - logger.info(f"执行反手做多,触发价: {trigger_price:.2f}") + logger.info(LOG_POSITION + f"执行反手做多,触发价: {trigger_price:.2f}") self.平仓() time.sleep(1) # 给交易所处理平仓的时间 # 轮询确认已无持仓再开多(最多等约 10 秒) @@ -590,13 +614,13 @@ class BitmartFuturesTransaction: if self.start != 0: logger.warning("反手做多:平仓后仍有持仓,放弃本次开多") return False - logger.info("已确认无持仓,执行开多") + logger.info(LOG_POSITION + "已确认无持仓,执行开多") self.开单(marketPriceLongOrder=1, size=size) time.sleep(3) if self.verify_position_direction(1): self.max_unrealized_pnl_seen = None - logger.success("反手做多成功") + logger.success(LOG_POSITION + "反手做多成功") self.last_reverse_time = time.time() time.sleep(20) return True @@ -606,7 +630,7 @@ class BitmartFuturesTransaction: elif signal_type == 'reverse_short': # 平多 + 开空(反手做空):先平仓,确认无仓后再开空 - logger.info(f"执行反手做空,触发价: {trigger_price:.2f}") + logger.info(LOG_POSITION + f"执行反手做空,触发价: {trigger_price:.2f}") self.平仓() time.sleep(1) for _ in range(10): @@ -616,13 +640,13 @@ class BitmartFuturesTransaction: if self.start != 0: logger.warning("反手做空:平仓后仍有持仓,放弃本次开空") return False - logger.info("已确认无持仓,执行开空") + logger.info(LOG_POSITION + "已确认无持仓,执行开空") self.开单(marketPriceLongOrder=-1, size=size) time.sleep(3) if self.verify_position_direction(-1): self.max_unrealized_pnl_seen = None - logger.success("反手做空成功") + logger.success(LOG_POSITION + "反手做空成功") self.last_reverse_time = time.time() time.sleep(20) return True @@ -635,7 +659,7 @@ class BitmartFuturesTransaction: def action(self): """主循环""" - logger.info("开始运行四分之一策略交易...") + logger.info(LOG_SYSTEM + "开始运行四分之一策略交易...") # 启动时设置全仓高杠杆 if not self.set_leverage(): @@ -650,7 +674,7 @@ class BitmartFuturesTransaction: # 打开浏览器 for i in range(5): if self.openBrowser(): - logger.info("浏览器打开成功") + logger.info(LOG_SYSTEM + "浏览器打开成功") break else: self.ding("打开浏览器失败!", error=True) @@ -668,26 +692,27 @@ class BitmartFuturesTransaction: # 1. 获取K线数据(当前K线和上一根K线) prev_kline, current_kline = self.get_klines() if not prev_kline or not current_kline: - logger.warning("获取K线失败,等待重试...") + logger.warning(LOG_SYSTEM + "获取K线失败,等待重试...") time.sleep(5) continue - # 记录进入新的K线 + # 记录进入新的K线(分块分隔,便于阅读) current_kline_time = current_kline['id'] if self.last_kline_time != current_kline_time: self.last_kline_time = current_kline_time - logger.info(f"进入新K线: {current_kline_time}") + logger.info("") + log_kline_header(current_kline_time) # 2. 获取当前价格 current_price = self.get_current_price() if not current_price: - logger.warning("获取价格失败,等待重试...") + logger.warning(LOG_SYSTEM + "获取价格失败,等待重试...") time.sleep(2) continue # 3. 每次循环都通过SDK获取真实持仓状态(避免状态不同步导致双向持仓) if not self.get_position_status(): - logger.warning("获取持仓状态失败,等待重试...") + logger.warning(LOG_SYSTEM + "获取持仓状态失败,等待重试...") time.sleep(2) continue @@ -711,14 +736,14 @@ class BitmartFuturesTransaction: kline_series = self.get_klines_series(35) ema10, ema20, atr14 = self.get_ema_atr_for_exit(kline_series) if ema10 is not None and current_price < ema10: - logger.info(f"多单 EMA10 平仓:当前价 {current_price:.2f} 跌破 EMA10 {ema10:.2f}(能赚点是点)") + logger.info(LOG_POSITION + f"多单 EMA10 平仓 | 价 {current_price:.2f} 跌破 EMA10 {ema10:.2f}") self.平仓() self.max_unrealized_pnl_seen = None self._candle_high_seen = None time.sleep(3) continue if atr14 is not None and self._candle_high_seen and (self._candle_high_seen - current_price) >= self.atr_multiplier * atr14: - logger.info(f"多单 ATR 追踪止盈:最高 {self._candle_high_seen:.2f},当前 {current_price:.2f},回撤 {(self._candle_high_seen - current_price):.2f} >= {self.atr_multiplier}×ATR(14)={atr14:.2f}") + logger.info(LOG_POSITION + f"多单 ATR 追踪止盈 | 最高 {self._candle_high_seen:.2f} 当前 {current_price:.2f} 回撤≥{self.atr_multiplier}×ATR={atr14:.2f}") self.平仓() self.max_unrealized_pnl_seen = None self._candle_high_seen = None @@ -730,14 +755,14 @@ class BitmartFuturesTransaction: kline_series = self.get_klines_series(35) ema10, ema20, atr14 = self.get_ema_atr_for_exit(kline_series) if ema10 is not None and current_price > ema10: - logger.info(f"空单 EMA10 平仓:当前价 {current_price:.2f} 涨破 EMA10 {ema10:.2f}(能赚点是点)") + logger.info(LOG_POSITION + f"空单 EMA10 平仓 | 价 {current_price:.2f} 涨破 EMA10 {ema10:.2f}") self.平仓() self.max_unrealized_pnl_seen = None self._candle_low_seen = None time.sleep(3) continue if atr14 is not None and self._candle_low_seen and (current_price - self._candle_low_seen) >= self.atr_multiplier * atr14: - logger.info(f"空单 ATR 追踪止盈:最低 {self._candle_low_seen:.2f},当前 {current_price:.2f},反弹 {(current_price - self._candle_low_seen):.2f} >= {self.atr_multiplier}×ATR(14)={atr14:.2f}") + logger.info(LOG_POSITION + f"空单 ATR 追踪止盈 | 最低 {self._candle_low_seen:.2f} 当前 {current_price:.2f} 反弹≥{self.atr_multiplier}×ATR={atr14:.2f}") self.平仓() self.max_unrealized_pnl_seen = None self._candle_low_seen = None @@ -764,7 +789,7 @@ class BitmartFuturesTransaction: else: pass # 涨幅不足,不启用动态回撤 if do_close: - logger.info(f"当前K线从最高点回落平仓:最高 {self._candle_high_seen:.2f},当前 {current_price:.2f},{reason}") + logger.info(LOG_POSITION + f"多单K线回落平仓 | 最高 {self._candle_high_seen:.2f} 当前 {current_price:.2f} | {reason}") self.平仓() self.max_unrealized_pnl_seen = None self._candle_high_seen = None @@ -786,7 +811,7 @@ class BitmartFuturesTransaction: do_close = True reason = f"跌幅 {rise_pct:.3f}% → 允许反弹 {drop_trigger_pct:.3f}%,实际反弹 {bounce_pct:.3f}%" if do_close: - logger.info(f"当前K线从最低点反弹平仓:最低 {self._candle_low_seen:.2f},当前 {current_price:.2f},{reason}") + logger.info(LOG_POSITION + f"空单K线反弹平仓 | 最低 {self._candle_low_seen:.2f} 当前 {current_price:.2f} | {reason}") self.平仓() self.max_unrealized_pnl_seen = None self._candle_low_seen = None @@ -797,7 +822,7 @@ class BitmartFuturesTransaction: if pnl_usd is not None: # 固定止损:亏损达到 3 美元平仓 if pnl_usd <= self.stop_loss_usd: - logger.info(f"仓位亏损 {pnl_usd:.2f} 美元 <= 止损 {self.stop_loss_usd} 美元,执行止损平仓") + logger.info(LOG_POSITION + f"固定止损平仓 | 亏损 {pnl_usd:.2f} 美元") self.平仓() self.max_unrealized_pnl_seen = None time.sleep(3) @@ -810,7 +835,7 @@ class BitmartFuturesTransaction: # 移动止损:盈利曾达到 activation 后,从最高盈利回撤 trailing_distance 则平仓 if self.max_unrealized_pnl_seen >= self.trailing_activation_usd: if pnl_usd < self.max_unrealized_pnl_seen - self.trailing_distance_usd: - logger.info(f"移动止损:当前盈利 {pnl_usd:.2f} 从最高 {self.max_unrealized_pnl_seen:.2f} 回撤 >= {self.trailing_distance_usd} 美元,平仓") + logger.info(LOG_POSITION + f"移动止损平仓 | 盈利 {pnl_usd:.2f} 从最高 {self.max_unrealized_pnl_seen:.2f} 回撤≥{self.trailing_distance_usd}$") self.平仓() self.max_unrealized_pnl_seen = None time.sleep(3) @@ -834,7 +859,7 @@ class BitmartFuturesTransaction: if signal: trade_success = self.execute_trade(signal) if trade_success: - logger.success(f"交易执行完成: {signal[0]}, 当前持仓状态: {self.start}") + logger.success(LOG_POSITION + f"交易执行完成: {signal[0]} | 当前持仓: {self.start}") page_start = True else: logger.warning(f"交易执行失败或被阻止: {signal[0]}") @@ -847,7 +872,7 @@ class BitmartFuturesTransaction: time.sleep(5) except KeyboardInterrupt: - logger.info("用户中断,程序退出") + logger.info(LOG_SYSTEM + "用户中断,程序退出") break except Exception as e: logger.error(f"主循环异常: {e}")