diff --git a/bitmart/四分之一,五分钟,反手条件充足修改版.py b/bitmart/四分之一,五分钟,反手条件充足修改版.py index 4bfd4c1..f75b132 100644 --- a/bitmart/四分之一,五分钟,反手条件充足修改版.py +++ b/bitmart/四分之一,五分钟,反手条件充足修改版.py @@ -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)