From aaa3a655bb20993c8d84766bc3e27efdde425b38 Mon Sep 17 00:00:00 2001 From: ddrwode <34234@3来 34> Date: Fri, 6 Feb 2026 15:48:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=9B=9E=E8=B0=83=E5=B9=B3?= =?UTF-8?q?=E4=BB=93=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../四分之一,五分钟,反手条件充足修改版.py | 105 +++++++++++++++++- 1 file changed, 102 insertions(+), 3 deletions(-) diff --git a/bitmart/四分之一,五分钟,反手条件充足修改版.py b/bitmart/四分之一,五分钟,反手条件充足修改版.py index 8c35284..c994fbd 100644 --- a/bitmart/四分之一,五分钟,反手条件充足修改版.py +++ b/bitmart/四分之一,五分钟,反手条件充足修改版.py @@ -54,6 +54,9 @@ class BitmartFuturesTransaction: self.retrace_ratio = 0.5 # 回撤系数,如 0.5 表示允许回撤涨幅的 50%(类似斐波那契 50% 回撤) self.min_rise_pct_to_activate = 0.02 # 至少涨/跌这么多才启用动态回撤,避免噪音 self.min_drop_pct_from_high = 0.03 # 至少从最高点回撤这么多%才平仓(保底,避免过于敏感) + # EMA(10) + EMA(20) / ATR(14) 平仓(多单优先):收盘跌破 EMA10 先平;或从最高价回撤 ≥ atr_multiplier×ATR(14) 平 + self.use_ema_atr_exit = True # 是否启用 EMA/ATR 平仓规则(多单) + self.atr_multiplier = 1.1 # 追踪止盈:从最高价回撤 ≥ 此倍数 × ATR(14) 则平仓 self._candle_high_seen = None # 当前K线内见过的最高价(多头用) self._candle_low_seen = None # 当前K线内见过的最低价(空头用) self._candle_id_for_high_low = None # 记录高低对应的K线 id,换线则重置 @@ -103,6 +106,78 @@ class BitmartFuturesTransaction: self.ding(text="获取K线异常", error=True) return None, None + def get_klines_series(self, count=35): + """获取最近 count 根 5 分钟 K 线(用于 EMA/ATR),按时间正序。最后一根为当前未收盘 K 线。""" + try: + end_time = int(time.time()) + response = self.contractAPI.get_kline( + contract_symbol=self.contract_symbol, + step=5, + start_time=end_time - 3600 * 4, + end_time=end_time + )[0]["data"] + formatted = [] + for k in response: + formatted.append({ + 'id': int(k["timestamp"]), + 'open': float(k["open_price"]), + 'high': float(k["high_price"]), + 'low': float(k["low_price"]), + 'close': float(k["close_price"]) + }) + formatted.sort(key=lambda x: x['id']) + return formatted[-count:] if len(formatted) >= count else formatted + except Exception as e: + logger.error(f"获取K线序列异常: {e}") + return [] + + @staticmethod + def _ema(series, period): + """对序列 series(从旧到新)计算 EMA(period),返回最后一个 EMA 值。""" + if not series or len(series) < period: + return None + alpha = 2.0 / (period + 1) + ema = sum(series[:period]) / period # 用前 period 根收盘的 SMA 做种子 + for i in range(period, len(series)): + ema = alpha * series[i] + (1 - alpha) * ema + return ema + + @staticmethod + def _atr(klines, period=14): + """对 klines(从旧到新,每项含 high/low/close)计算 ATR(period),返回最后一个 ATR 值。""" + if not klines or len(klines) < period + 1: + return None + tr_list = [] + for i in range(1, len(klines)): + high, low = klines[i]['high'], klines[i]['low'] + prev_close = klines[i - 1]['close'] + tr = max(high - low, abs(high - prev_close), abs(low - prev_close)) + tr_list.append(tr) + if len(tr_list) < period: + return None + # ATR = EMA(TR, period) + alpha = 2.0 / (period + 1) + atr = sum(tr_list[:period]) / period + for i in range(period, len(tr_list)): + atr = alpha * tr_list[i] + (1 - alpha) * atr + return atr + + def get_ema_atr_for_exit(self, kline_series): + """ + 基于已收盘 K 线计算 EMA10、EMA20、ATR(14)。 + kline_series 最后一根可为当前未收盘 K 线,计算时用倒数第 2~N 根作为已收盘。 + 返回 (ema10, ema20, atr14),任一不足则对应为 None。 + """ + if not kline_series or len(kline_series) < 21: + return None, None, None + # 用除最后一根外的已收盘 K 线(若只有 21 根则用前 20 根) + closed = kline_series[:-1] if len(kline_series) >= 21 else kline_series[:20] + closes = [k['close'] for k in closed] + ema10 = self._ema(closes, 10) + ema20 = self._ema(closes, 20) + atr14 = self._atr(closed, 14) + return ema10, ema20, atr14 + def get_current_price(self): """获取当前最新价格""" try: @@ -622,18 +697,42 @@ class BitmartFuturesTransaction: logger.debug(f"当前持仓状态: {self.start} (0=无, 1=多, -1=空)") - # 3.5 止损/止盈/移动止损 + 当前K线从极值回落平仓 + # 3.5 止损/止盈/移动止损 + EMA/ATR 平仓 + 当前K线从极值回落平仓 if self.start != 0: # 当前K线从最高/最低点回落平仓:换线重置跟踪,有持仓时更新本K线内最高/最低价并检查 if self._candle_id_for_high_low != current_kline_time: self._candle_high_seen = None self._candle_low_seen = None self._candle_id_for_high_low = current_kline_time + # 多头:先更新本 K 线最高价(供 ATR 追踪与后续回落逻辑用) + if self.start == 1: + self._candle_high_seen = max(self._candle_high_seen or 0, current_price) + elif self.start == -1: + self._candle_low_seen = min(self._candle_low_seen or float('inf'), current_price) + + # 多单优先:EMA(10) + ATR(14) 平仓 —— 收盘跌破 EMA10 先平;或从最高价回撤 ≥ 1.1×ATR 平 + if 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) + if ema10 is not None and current_price < ema10: + logger.info(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}") + self.平仓() + self.max_unrealized_pnl_seen = None + self._candle_high_seen = None + time.sleep(3) + continue + use_fixed = self.drop_from_high_mode == 'fixed' and self.drop_from_high_to_close and self.drop_from_high_to_close > 0 use_pct = self.drop_from_high_mode == 'pct_retrace' if use_fixed or use_pct: - if self.start == 1: # 多头:跟踪当前K线最高价,从最高点回落超过阈值则平仓 - self._candle_high_seen = max(self._candle_high_seen or 0, current_price) + if self.start == 1: # 多头:最高价已在上面更新,这里只做回落判断 do_close = False if use_fixed and self._candle_high_seen and current_price <= self._candle_high_seen - self.drop_from_high_to_close: do_close = True