日志展示优化
This commit is contained in:
@@ -74,8 +74,10 @@ def make_metrics_panel(state: dict) -> Panel:
|
||||
t.add_row("突破做多", f"{state.get('long_breakout', 0):.2f}")
|
||||
t.add_row("突破做空", f"{state.get('short_breakout', 0):.2f}")
|
||||
ema10 = state.get("ema10")
|
||||
ema20 = state.get("ema20")
|
||||
atr14 = state.get("atr14")
|
||||
t.add_row("EMA10", f"{ema10:.2f}" if ema10 is not None else "-")
|
||||
t.add_row("EMA20", f"{ema20:.2f}" if ema20 is not None else "-")
|
||||
t.add_row("ATR14", f"{atr14:.2f}" if atr14 is not None else "-")
|
||||
pnl = state.get("unrealized_pnl")
|
||||
t.add_row("未实现盈亏", f"{pnl:.2f}$" if pnl is not None else "-")
|
||||
@@ -132,13 +134,11 @@ class BitmartFuturesTransaction:
|
||||
self.leverage = "100" # 高杠杆(全仓模式下可开更大仓位)
|
||||
self.open_type = "cross" # 全仓模式
|
||||
self.risk_percent = 0 # 未使用;若启用则可为每次开仓占可用余额的百分比
|
||||
self.trailing_activation_usd = 6 # 盈利达到此金额后启动移动止损(调高避免波动不大就触发)
|
||||
self.trailing_distance_usd = 3 # 从最高盈利回撤此金额则平仓(调高避免小幅回撤就平)
|
||||
self.max_unrealized_pnl_seen = None # 持仓期间见过的最大盈利(用于移动止损)
|
||||
# EMA(10) + ATR(14) 平仓(多/空一致):多单跌破 EMA10 或从最高回撤≥ATR 平;空单涨破 EMA10 或从最低反弹≥ATR 平
|
||||
# 退出:仅 EMA10(快退出)+ 1.1×ATR(14) 追踪。EMA20 不做先平,只做趋势/方向过滤(见下)
|
||||
self.use_ema_atr_exit = True # 是否启用 EMA/ATR 平仓规则(多单+空单)
|
||||
self.atr_multiplier = 1.8 # 追踪止盈:从极值回撤/反弹 ≥ 此倍数×ATR(14) 则平仓(调高避免波动不大就平)
|
||||
self.min_hold_seconds = 90 # 开仓/反手后至少持仓此时长才允许技术性止盈(EMA/ATR/移动止损)
|
||||
self.atr_multiplier = 1.1 # 追踪止盈:从当前K线极值回撤/反弹 ≥ 此倍数×ATR(14) 则平仓
|
||||
self.use_ema20_filter = True # 是否用 EMA20 做开仓/反手方向过滤 + 持仓强制减仓(趋势过滤)
|
||||
self.min_hold_seconds = 90 # 开仓/反手后至少持仓此时长才允许技术性止盈(EMA10/EMA20/ATR)
|
||||
self._candle_high_seen = None # 当前K线内见过的最高价(多头用)
|
||||
self._candle_low_seen = None # 当前K线内见过的最低价(空头用)
|
||||
self._candle_id_for_high_low = None # 记录高低对应的K线 id,换线则重置
|
||||
@@ -527,14 +527,14 @@ class BitmartFuturesTransaction:
|
||||
|
||||
self._display_triggers = {"long_trigger": long_trigger, "short_trigger": short_trigger, "long_breakout": long_breakout, "short_breakout": short_breakout}
|
||||
|
||||
# 无持仓时检查开仓信号
|
||||
# 无持仓时检查开仓信号(做多看突破价,做空看做空触发价,与反手阈值一致便于回落即进空)
|
||||
if self.start == 0:
|
||||
if current_price >= long_breakout and not skip_long_by_lower_third:
|
||||
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(LOG_SIGNAL + f"触发做空 | 价 {current_price:.2f} <= 突破价 {short_breakout:.2f}")
|
||||
return ('short', short_breakout)
|
||||
elif current_price <= short_trigger and not skip_short_by_upper_third:
|
||||
logger.info(LOG_SIGNAL + f"触发做空 | 价 {current_price:.2f} <= 做空触发价 {short_trigger:.2f}")
|
||||
return ('short', short_trigger)
|
||||
|
||||
# 持仓时检查反手信号
|
||||
elif self.start == 1: # 持多仓
|
||||
@@ -630,7 +630,6 @@ 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(LOG_POSITION + "开多成功")
|
||||
@@ -655,7 +654,6 @@ 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(LOG_POSITION + "开空成功")
|
||||
@@ -682,7 +680,6 @@ class BitmartFuturesTransaction:
|
||||
time.sleep(3)
|
||||
|
||||
if self.verify_position_direction(1):
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self.last_open_time = time.time() # 反手后的新仓位也受最短持仓时间保护
|
||||
logger.success(LOG_POSITION + "反手做多成功")
|
||||
time.sleep(20)
|
||||
@@ -708,7 +705,6 @@ class BitmartFuturesTransaction:
|
||||
time.sleep(3)
|
||||
|
||||
if self.verify_position_direction(-1):
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self.last_open_time = time.time() # 反手后的新仓位也受最短持仓时间保护
|
||||
logger.success(LOG_POSITION + "反手做空成功")
|
||||
time.sleep(20)
|
||||
@@ -828,15 +824,16 @@ class BitmartFuturesTransaction:
|
||||
kline_series = self.get_klines_series(35)
|
||||
ema10, ema20, atr14 = self.get_ema_atr_for_exit(kline_series)
|
||||
self._display_state["ema10"] = ema10
|
||||
self._display_state["ema20"] = ema20
|
||||
self._display_state["atr14"] = atr14
|
||||
if getattr(self, "_display_triggers", None):
|
||||
self._display_state.update(self._display_triggers)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3.5 移动止损 + EMA/ATR 平仓
|
||||
# 3.5 平仓:EMA10 快退出、EMA20 趋势过滤强制减仓、1.1×ATR 追踪
|
||||
if self.start != 0:
|
||||
# 换线重置跟踪,有持仓时更新本K线内最高/最低价(供 ATR 追踪用)
|
||||
# 换线重置跟踪,有持仓时更新本K线内最高/最低价
|
||||
if self._candle_id_for_high_low != current_kline_time:
|
||||
self._candle_high_seen = None
|
||||
self._candle_low_seen = None
|
||||
@@ -850,64 +847,63 @@ class BitmartFuturesTransaction:
|
||||
hold_sec = (time.time() - self.last_open_time) if self.last_open_time else 999999
|
||||
allow_technical_exit = hold_sec >= self.min_hold_seconds
|
||||
|
||||
# 多单:EMA(10) + ATR(14) 平仓(需满最短持仓时间)
|
||||
# 多单平仓:EMA10 快退出 / EMA20 趋势过滤强制减仓 / 1.1×ATR 追踪(组合1:能赚点是点)
|
||||
if allow_technical_exit and self.start == 1 and self.use_ema_atr_exit:
|
||||
kline_series = self.get_klines_series(35)
|
||||
ema10, ema20, atr14 = self.get_ema_atr_for_exit(kline_series)
|
||||
# 1) 价格跌破 EMA10 → 先平(能赚点是点)
|
||||
if ema10 is not None and current_price < ema10:
|
||||
logger.info(LOG_POSITION + f"多单 EMA10 平仓 | 价 {current_price:.2f} 跌破 EMA10 {ema10:.2f}")
|
||||
self.平仓()
|
||||
self._last_exit_kline_id = current_kline_time
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_high_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
# 2) EMA20 趋势过滤:价格跌破 EMA20 → 强制减仓(方向过滤,不做先平触发)
|
||||
if self.use_ema20_filter and ema20 is not None and current_price < ema20:
|
||||
logger.info(LOG_POSITION + f"多单 EMA20 强制减仓 | 价 {current_price:.2f} 跌破 EMA20 {ema20:.2f}")
|
||||
self.平仓()
|
||||
self._last_exit_kline_id = current_kline_time
|
||||
self._candle_high_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
# 3) 从最高价回撤 ≥ 1.1×ATR(14) → 平仓
|
||||
if atr14 is not None and self._candle_high_seen and (self._candle_high_seen - current_price) >= self.atr_multiplier * atr14:
|
||||
logger.info(LOG_POSITION + f"多单 ATR 追踪止盈 | 最高 {self._candle_high_seen:.2f} 当前 {current_price:.2f} 回撤≥{self.atr_multiplier}×ATR={atr14:.2f}")
|
||||
self.平仓()
|
||||
self._last_exit_kline_id = current_kline_time
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_high_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
# 空单:EMA(10) + ATR(14) 平仓(需满最短持仓时间)
|
||||
# 空单平仓:EMA10 快退出 / EMA20 趋势过滤强制减仓 / 1.1×ATR 追踪
|
||||
if allow_technical_exit and self.start == -1 and self.use_ema_atr_exit:
|
||||
kline_series = self.get_klines_series(35)
|
||||
ema10, ema20, atr14 = self.get_ema_atr_for_exit(kline_series)
|
||||
# 1) 价格涨破 EMA10 → 先平
|
||||
if ema10 is not None and current_price > ema10:
|
||||
logger.info(LOG_POSITION + f"空单 EMA10 平仓 | 价 {current_price:.2f} 涨破 EMA10 {ema10:.2f}")
|
||||
self.平仓()
|
||||
self._last_exit_kline_id = current_kline_time
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_low_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
# 2) EMA20 趋势过滤:价格涨破 EMA20 → 强制减仓
|
||||
if self.use_ema20_filter and ema20 is not None and current_price > ema20:
|
||||
logger.info(LOG_POSITION + f"空单 EMA20 强制减仓 | 价 {current_price:.2f} 涨破 EMA20 {ema20:.2f}")
|
||||
self.平仓()
|
||||
self._last_exit_kline_id = current_kline_time
|
||||
self._candle_low_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
# 3) 从最低价反弹 ≥ 1.1×ATR(14) → 平仓
|
||||
if atr14 is not None and self._candle_low_seen and (current_price - self._candle_low_seen) >= self.atr_multiplier * atr14:
|
||||
logger.info(LOG_POSITION + f"空单 ATR 追踪止盈 | 最低 {self._candle_low_seen:.2f} 当前 {current_price:.2f} 反弹≥{self.atr_multiplier}×ATR={atr14:.2f}")
|
||||
self.平仓()
|
||||
self._last_exit_kline_id = current_kline_time
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_low_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
pnl_usd = self.get_unrealized_pnl_usd()
|
||||
if pnl_usd is not None:
|
||||
# 更新持仓期间最大盈利(用于移动止损)
|
||||
if self.max_unrealized_pnl_seen is None:
|
||||
self.max_unrealized_pnl_seen = pnl_usd
|
||||
else:
|
||||
self.max_unrealized_pnl_seen = max(self.max_unrealized_pnl_seen, pnl_usd)
|
||||
# 移动止损:盈利曾达到 activation 后,从最高盈利回撤 trailing_distance 则平仓(需满最短持仓时间)
|
||||
if allow_technical_exit and self.max_unrealized_pnl_seen >= self.trailing_activation_usd:
|
||||
if pnl_usd < self.max_unrealized_pnl_seen - self.trailing_distance_usd:
|
||||
logger.info(LOG_POSITION + f"移动止损平仓 | 盈利 {pnl_usd:.2f} 从最高 {self.max_unrealized_pnl_seen:.2f} 回撤≥{self.trailing_distance_usd}$")
|
||||
self.平仓()
|
||||
self._last_exit_kline_id = current_kline_time
|
||||
self.max_unrealized_pnl_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
# 4. 检查信号
|
||||
signal = self.check_signal(current_price, prev_kline, current_kline)
|
||||
|
||||
@@ -926,6 +922,18 @@ class BitmartFuturesTransaction:
|
||||
else:
|
||||
self._current_kline_id_for_open = current_kline_time # 供 execute_trade 成功后记录
|
||||
|
||||
# 5.6 EMA20 方向过滤:价在 EMA20 上方才开多,下方才开空(趋势过滤,暂停逆势开仓)
|
||||
if signal and self.use_ema20_filter:
|
||||
kline_series = self.get_klines_series(35)
|
||||
_, ema20, _ = self.get_ema_atr_for_exit(kline_series)
|
||||
if ema20 is not None:
|
||||
if signal[0] in ('long', 'reverse_long') and current_price <= ema20:
|
||||
self._log_throttled("ema20_no_long", LOG_SYSTEM + f"EMA20 方向过滤:价 {current_price:.2f} 未在 EMA20 {ema20:.2f} 上方,暂停开多", interval=2.0)
|
||||
signal = None
|
||||
elif signal[0] in ('short', 'reverse_short') and current_price >= ema20:
|
||||
self._log_throttled("ema20_no_short", LOG_SYSTEM + f"EMA20 方向过滤:价 {current_price:.2f} 未在 EMA20 {ema20:.2f} 下方,暂停开空", interval=2.0)
|
||||
signal = None
|
||||
|
||||
# 6. 有信号则执行交易
|
||||
if signal:
|
||||
trade_success = self.execute_trade(signal)
|
||||
|
||||
Reference in New Issue
Block a user