From a6df435c2f84355b7c798c5be089b97ae3062d78 Mon Sep 17 00:00:00 2001 From: ddrwode <34234@3来 34> Date: Tue, 10 Feb 2026 16:03:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A5=E5=BF=97=E5=B1=95=E7=A4=BA=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bitmart/交易.py | 159 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 105 insertions(+), 54 deletions(-) diff --git a/bitmart/交易.py b/bitmart/交易.py index ad60462..c77bde2 100644 --- a/bitmart/交易.py +++ b/bitmart/交易.py @@ -67,15 +67,13 @@ class BitmartFuturesTransaction: # 策略优化参数 self.min_prev_entity_pct = 0.1 # 上一根K线实体涨幅(%),仅当实体涨幅 > 此值才用作“上一根” - # 多仓止盈:基于当前K线开盘价,涨 0.4% 后回调 0.3% 平仓,涨回 0.2% 同向开多 - self.take_profit_rise_pct = 0.4 # 触发止盈的涨幅(%) - self.take_profit_retrace_pct = 0.3 # 回调此幅度(%)则平仓 - self.reentry_rise_pct = 0.2 # 平仓后涨回此幅度(%)则开多 - self.take_profit_triggered_kline_id = None # 本K线内是否已出现过“涨 0.4%” - self.take_profit_reentry_threshold = None # 止盈平仓后,价格 >= 此值则开多(同向) - # 空仓止盈:跌 0.4% 后反弹 0.3% 平空,再跌到 0.2% 同向开空(复用上面三个百分比) - self.take_profit_triggered_kline_id_short = None # 本K线内是否已出现过“跌 0.4%” - self.take_profit_reentry_threshold_short = None # 止盈平空后,价格 <= 此值则开空(同向) + # 自动止盈:基于当前K线振幅四等分(多仓用 open~high,空仓用 open~low) + self.take_profit_close_quartile = 3 # 达到极值后,回到 3/4 位置平仓 + self.take_profit_reentry_quartile = 2 # 止盈后,继续回到 2/4 位置同向再开仓 + self.take_profit_triggered_kline_id = None # 多仓:本K线内是否出现过 open~high 的极值触发 + self.take_profit_reentry_threshold = None # 多仓止盈平仓后,价格 <= 此值则开多(同向) + self.take_profit_triggered_kline_id_short = None # 空仓:本K线内是否出现过 open~low 的极值触发 + self.take_profit_reentry_threshold_short = None # 空仓止盈平仓后,价格 >= 此值则开空(同向) self.optimized_params_file = Path(__file__).resolve().parent / "atr_best_params.json" self.strategy_log_dir = Path(__file__).resolve().parent # 策略开仓日志目录 @@ -358,6 +356,52 @@ class BitmartFuturesTransaction: 'lower': min(kline['open'], kline['close']) # 实体下边 } + def calc_long_take_profit_levels(self, current_kline): + """ + 多仓止盈阈值(自动四等分): + 基于当前K线 open~high 计算: + - extreme: high(4/4 极值) + - close: open + 3/4 * (high-open) + - reentry: open + 2/4 * (high-open) + """ + k_open = float(current_kline['open']) + k_high = float(current_kline['high']) + if k_high <= k_open: + return None + + move = k_high - k_open + close_threshold = k_open + move * (self.take_profit_close_quartile / 4) + reentry_threshold = k_open + move * (self.take_profit_reentry_quartile / 4) + return { + "open": k_open, + "extreme": k_high, + "close_threshold": close_threshold, + "reentry_threshold": reentry_threshold + } + + def calc_short_take_profit_levels(self, current_kline): + """ + 空仓止盈阈值(自动四等分): + 基于当前K线 open~low 计算: + - extreme: low(4/4 极值) + - close: open - 3/4 * (open-low) + - reentry: open - 2/4 * (open-low) + """ + k_open = float(current_kline['open']) + k_low = float(current_kline['low']) + if k_low >= k_open: + return None + + move = k_open - k_low + close_threshold = k_open - move * (self.take_profit_close_quartile / 4) + reentry_threshold = k_open - move * (self.take_profit_reentry_quartile / 4) + return { + "open": k_open, + "extreme": k_low, + "close_threshold": close_threshold, + "reentry_threshold": reentry_threshold + } + def check_signal(self, current_price, prev_kline, current_kline): """ 检查交易信号 @@ -692,36 +736,39 @@ class BitmartFuturesTransaction: logger.debug(f"当前持仓状态: {self.start} (0=无, 1=多, -1=空)") - kline_open = current_kline['open'] signal = None - # 3.5 多仓止盈:当前K线涨 0.4% 后回调 0.3% 则平仓 + # 3.5 多仓止盈(自动四等分):open~high 达到极值后,回到 3/4 平仓 if self.start == 1: - rise_threshold = kline_open * (1 + self.take_profit_rise_pct / 100) - retrace_close_threshold = kline_open * (1 + (self.take_profit_rise_pct - self.take_profit_retrace_pct) / 100) - if current_price >= rise_threshold: - self.take_profit_triggered_kline_id = current_kline_time - if ( - self.take_profit_triggered_kline_id == current_kline_time - and current_price <= retrace_close_threshold - ): - reason = ( - f"当前K线开盘价={kline_open:.2f},曾涨至≥{rise_threshold:.2f}(+{self.take_profit_rise_pct}%)," - f"现价{current_price:.2f}已回调至≤{retrace_close_threshold:.2f}(+{self.take_profit_rise_pct - self.take_profit_retrace_pct}%),按规则止盈平多" - ) - self._log_take_profit_action("止盈平多仓", reason) - self.平仓() - self.take_profit_reentry_threshold = kline_open * (1 + self.reentry_rise_pct / 100) - self.take_profit_triggered_kline_id = None - page_start = True - time.sleep(1) + long_levels = self.calc_long_take_profit_levels(current_kline) + if long_levels is not None: + retrace_close_threshold = long_levels["close_threshold"] + # 使用当前K线 high 判定是否已触发到 4/4 极值,避免依赖分钟收盘价漏判 + if long_levels["extreme"] > long_levels["open"]: + self.take_profit_triggered_kline_id = current_kline_time + if ( + self.take_profit_triggered_kline_id == current_kline_time + and current_price <= retrace_close_threshold + ): + reason = ( + f"当前K线开盘价={long_levels['open']:.2f},最高价={long_levels['extreme']:.2f}(4/4);" + f"现价{current_price:.2f}已回调至≤{retrace_close_threshold:.2f}(3/4),按规则止盈平多" + ) + self._log_take_profit_action("止盈平多仓", reason) + self.平仓() + self.take_profit_reentry_threshold = long_levels["reentry_threshold"] + self.take_profit_triggered_kline_id = None + page_start = True + time.sleep(1) + else: + signal = self.check_signal(current_price, prev_kline, current_kline) else: signal = self.check_signal(current_price, prev_kline, current_kline) - # 3.6 止盈平多后:价格涨回 0.2% 则同向开多 + # 3.6 止盈平多后:价格继续回调到 2/4 则同向开多 elif self.start == 0 and self.take_profit_reentry_threshold is not None: - if current_price >= self.take_profit_reentry_threshold: + if current_price <= self.take_profit_reentry_threshold: reason = ( - f"止盈平仓后价格涨回≥{self.take_profit_reentry_threshold:.2f}(相对当根K线开盘+{self.reentry_rise_pct}%),按规则同向开多" + f"止盈平仓后价格继续回调至≤{self.take_profit_reentry_threshold:.2f}(2/4),按规则同向开多" ) self._log_take_profit_action("止盈后同向开多", reason) self.开单(marketPriceLongOrder=1, size=self.default_order_size) @@ -737,33 +784,37 @@ class BitmartFuturesTransaction: self.take_profit_reentry_threshold = None else: signal = self.check_signal(current_price, prev_kline, current_kline) - # 3.7 空仓止盈:当前K线跌 0.4% 后反弹 0.3% 则平空 + # 3.7 空仓止盈(自动四等分):open~low 达到极值后,回到 3/4 平仓 elif self.start == -1: - drop_threshold = kline_open * (1 - self.take_profit_rise_pct / 100) - retrace_close_threshold_short = kline_open * (1 - (self.take_profit_rise_pct - self.take_profit_retrace_pct) / 100) - if current_price <= drop_threshold: - self.take_profit_triggered_kline_id_short = current_kline_time - if ( - self.take_profit_triggered_kline_id_short == current_kline_time - and current_price >= retrace_close_threshold_short - ): - reason = ( - f"当前K线开盘价={kline_open:.2f},曾跌至≤{drop_threshold:.2f}(-{self.take_profit_rise_pct}%)," - f"现价{current_price:.2f}已反弹至≥{retrace_close_threshold_short:.2f}(-{self.take_profit_rise_pct - self.take_profit_retrace_pct}%),按规则止盈平空" - ) - self._log_take_profit_action("止盈平空仓", reason) - self.平仓() - self.take_profit_reentry_threshold_short = kline_open * (1 - self.reentry_rise_pct / 100) - self.take_profit_triggered_kline_id_short = None - page_start = True - time.sleep(1) + short_levels = self.calc_short_take_profit_levels(current_kline) + if short_levels is not None: + retrace_close_threshold_short = short_levels["close_threshold"] + # 使用当前K线 low 判定是否已触发到 4/4 极值,避免依赖分钟收盘价漏判 + if short_levels["extreme"] < short_levels["open"]: + self.take_profit_triggered_kline_id_short = current_kline_time + if ( + self.take_profit_triggered_kline_id_short == current_kline_time + and current_price >= retrace_close_threshold_short + ): + reason = ( + f"当前K线开盘价={short_levels['open']:.2f},最低价={short_levels['extreme']:.2f}(4/4);" + f"现价{current_price:.2f}已反弹至≥{retrace_close_threshold_short:.2f}(3/4),按规则止盈平空" + ) + self._log_take_profit_action("止盈平空仓", reason) + self.平仓() + self.take_profit_reentry_threshold_short = short_levels["reentry_threshold"] + self.take_profit_triggered_kline_id_short = None + page_start = True + time.sleep(1) + else: + signal = self.check_signal(current_price, prev_kline, current_kline) else: signal = self.check_signal(current_price, prev_kline, current_kline) - # 3.8 止盈平空后:价格再跌到 0.2% 则同向开空 + # 3.8 止盈平空后:价格继续反弹到 2/4 则同向开空 elif self.start == 0 and self.take_profit_reentry_threshold_short is not None: - if current_price <= self.take_profit_reentry_threshold_short: + if current_price >= self.take_profit_reentry_threshold_short: reason = ( - f"止盈平空后价格再跌至≤{self.take_profit_reentry_threshold_short:.2f}(相对当根K线开盘-{self.reentry_rise_pct}%),按规则同向开空" + f"止盈平空后价格继续反弹至≥{self.take_profit_reentry_threshold_short:.2f}(2/4),按规则同向开空" ) self._log_take_profit_action("止盈后同向开空", reason) self.开单(marketPriceLongOrder=-1, size=self.default_order_size)