优化日志展示

This commit is contained in:
ddrwode
2026-02-06 16:07:02 +08:00
parent a54e7d1823
commit eae69f7c8a

View File

@@ -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}")