From bfcc6161b6b161e0c49397fa9354d0b870532ab9 Mon Sep 17 00:00:00 2001 From: 27942 <1313123@342> Date: Sun, 8 Feb 2026 15:54:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=8C=E7=BE=8E=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E4=BC=98=E5=8C=96=E8=AE=A1=E7=AE=97=E7=9B=88?= =?UTF-8?q?=E4=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../三分之一,五分钟,反手条件充足修改版.py | 251 ++---------------- bitmart/四分之一策略说明-修改版.md | 192 -------------- 2 files changed, 22 insertions(+), 421 deletions(-) delete mode 100644 bitmart/四分之一策略说明-修改版.md diff --git a/bitmart/三分之一,五分钟,反手条件充足修改版.py b/bitmart/三分之一,五分钟,反手条件充足修改版.py index 3e92fa4..749bff6 100644 --- a/bitmart/三分之一,五分钟,反手条件充足修改版.py +++ b/bitmart/三分之一,五分钟,反手条件充足修改版.py @@ -28,16 +28,6 @@ class BitmartFuturesTransaction: self.last_kline_time = None # 上一次处理的K线时间戳,用于判断是否是新K线 - # 反手频率控制 - self.reverse_cooldown_seconds = 1.5 * 60 # 反手冷却时间(秒) - self.reverse_min_move_pct = 0.05 # 反手最小价差过滤(百分比) - self.last_reverse_time = None # 上次反手时间 - - # 开仓频率控制 - self.open_cooldown_seconds = 60 # 开仓冷却时间(秒),两次开仓至少间隔此时长 - self.last_open_time = None # 上次开仓时间 - self.last_open_kline_id = None # 上次开仓所在 K 线 id,同一根 K 线只允许开仓一次 - self.leverage = "100" # 高杠杆(全仓模式下可开更大仓位) self.open_type = "cross" # 全仓模式 self.risk_percent = 0 # 未使用;若启用则可为每次开仓占可用余额的百分比 @@ -48,12 +38,6 @@ class BitmartFuturesTransaction: self.bit_id = bit_id self.default_order_size = 25 # 开仓/反手张数,统一在此修改 - # ATR 回调止盈参数 - self.atr_period = 14 # ATR 周期 - self.atr_multiplier = 2.5 # ATR 倍数(稳健 2.5~3,激进 1.5~2) - self.highest_since_entry = None # 开仓以来最高价(多单追踪) - self.lowest_since_entry = None # 开仓以来最低价(空单追踪) - # 策略相关变量 self.prev_kline = None # 上一根K线 self.current_kline = None # 当前K线 @@ -93,33 +77,6 @@ class BitmartFuturesTransaction: self.ding(text="获取K线异常", error=True) return None, None - def get_all_klines(self): - """获取所有可用的5分钟K线(用于ATR计算等),按时间升序排列""" - try: - end_time = int(time.time()) - response = self.contractAPI.get_kline( - contract_symbol=self.contract_symbol, - step=5, - start_time=end_time - 3600 * 3, - 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 - except Exception as e: - logger.error(f"获取K线异常: {e}") - self.ding(text="获取K线异常", error=True) - return [] - def get_current_price(self): """获取当前最新价格""" try: @@ -182,14 +139,6 @@ class BitmartFuturesTransaction: logger.error(f"持仓查询异常: {e}") return False - def get_unrealized_pnl_usd(self): - """ - 获取当前持仓未实现盈亏(美元),直接使用API返回值 - """ - if self.start == 0 or self.unrealized_pnl is None: - return None - return self.unrealized_pnl - def set_leverage(self): """程序启动时设置全仓 + 高杠杆""" try: @@ -299,82 +248,6 @@ class BitmartFuturesTransaction: 'lower': min(kline['open'], kline['close']) # 实体下边 } - def calculate_atr(self, klines): - """ - 计算 ATR(14) - klines: 按时间升序的完整K线列表(含当前未收盘K线) - 返回: ATR 值或 None - """ - # 排除最后一根(当前未收盘),使用已收盘K线 - closed = klines[:-1] if len(klines) > 1 else klines - if len(closed) < self.atr_period + 1: - logger.warning(f"已收盘K线不足({len(closed)}),需要至少 {self.atr_period + 1} 根,无法计算ATR") - return None - - # 计算 True Range 序列 - true_ranges = [] - for i in range(1, len(closed)): - h = closed[i]['high'] - l = closed[i]['low'] - pc = closed[i - 1]['close'] - tr = max(h - l, abs(h - pc), abs(l - pc)) - true_ranges.append(tr) - - # 取最近 atr_period 个 TR 的简单平均作为 ATR - recent_trs = true_ranges[-self.atr_period:] - atr = sum(recent_trs) / len(recent_trs) - return atr - - def check_trailing_stop(self, current_price, atr): - """ - 检查 ATR 回调止盈是否触发 - - 多单: 从开仓以来最高价回落 >= ATR * k → 平仓 - - 空单: 从开仓以来最低价反弹 >= ATR * k → 平仓 - 返回: True=触发止盈, False=未触发 - """ - if self.start == 0 or atr is None: - return False - - if self.start == 1: # 持多仓 - # 首次追踪(程序重启等情况),从开仓均价开始 - if self.highest_since_entry is None: - self.highest_since_entry = self.open_avg_price if self.open_avg_price else current_price - if current_price > self.highest_since_entry: - self.highest_since_entry = current_price - - trailing_stop = self.highest_since_entry - atr * self.atr_multiplier - logger.debug(f"[ATR止盈] 多单 | 最高={self.highest_since_entry:.2f} | " - f"ATR={atr:.4f} | k={self.atr_multiplier} | " - f"止盈线={trailing_stop:.2f} | 当前={current_price:.2f}") - - if current_price <= trailing_stop: - logger.info(f"ATR回调止盈触发(多)! 当前价 {current_price:.2f} <= " - f"止盈线 {trailing_stop:.2f} " - f"(最高 {self.highest_since_entry:.2f} - " - f"{self.atr_multiplier} x ATR {atr:.4f})") - return True - - elif self.start == -1: # 持空仓 - # 首次追踪(程序重启等情况),从开仓均价开始 - if self.lowest_since_entry is None: - self.lowest_since_entry = self.open_avg_price if self.open_avg_price else current_price - if current_price < self.lowest_since_entry: - self.lowest_since_entry = current_price - - trailing_stop = self.lowest_since_entry + atr * self.atr_multiplier - logger.debug(f"[ATR止盈] 空单 | 最低={self.lowest_since_entry:.2f} | " - f"ATR={atr:.4f} | k={self.atr_multiplier} | " - f"止盈线={trailing_stop:.2f} | 当前={current_price:.2f}") - - if current_price >= trailing_stop: - logger.info(f"ATR回调止盈触发(空)! 当前价 {current_price:.2f} >= " - f"止盈线 {trailing_stop:.2f} " - f"(最低 {self.lowest_since_entry:.2f} + " - f"{self.atr_multiplier} x ATR {atr:.4f})") - return True - - return False - def check_signal(self, current_price, prev_kline, current_kline): """ 检查交易信号 @@ -405,17 +278,17 @@ class BitmartFuturesTransaction: if use_current_open_as_base: # 以当前K线开盘价为基准计算(跳空时用当前开盘价参与计算) calc_lower = current_kline['open'] - calc_upper = current_kline['open'] # 同一基准,上下三分之一对称 - long_trigger = calc_lower + prev_entity / 3 - short_trigger = calc_upper - prev_entity / 3 - long_breakout = calc_upper + prev_entity / 3 - short_breakout = calc_lower - prev_entity / 3 + calc_upper = current_kline['open'] # 同一基准,上下四分之一对称 + long_trigger = calc_lower + prev_entity / 4 + short_trigger = calc_upper - prev_entity / 4 + long_breakout = calc_upper + prev_entity / 4 + short_breakout = calc_lower - prev_entity / 4 else: # 原有计算方式 - long_trigger = prev_entity_lower + prev_entity / 3 # 做多触发价 = 实体下边 + 实体/3(下三分之一处) - short_trigger = prev_entity_upper - prev_entity / 3 # 做空触发价 = 实体上边 - 实体/3(上三分之一处) - long_breakout = prev_entity_upper + prev_entity / 3 # 做多突破价 = 实体上边 + 实体/3 - short_breakout = prev_entity_lower - prev_entity / 3 # 做空突破价 = 实体下边 - 实体/3 + long_trigger = prev_entity_lower + prev_entity / 4 # 做多触发价 = 实体下边 + 实体/4(下四分之一处) + short_trigger = prev_entity_upper - prev_entity / 4 # 做空触发价 = 实体上边 - 实体/4(上四分之一处) + long_breakout = prev_entity_upper + prev_entity / 4 # 做多突破价 = 实体上边 + 实体/4 + short_breakout = prev_entity_lower - prev_entity / 4 # 做空突破价 = 实体下边 - 实体/4 # 上一根阴线 + 当前阳线:做多形态,不按上一根K线上三分之一做空 prev_is_bearish = prev_kline['close'] < prev_kline['open'] @@ -433,27 +306,27 @@ class BitmartFuturesTransaction: 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/3): {long_trigger:.2f}, 做空触发价(上1/3): {short_trigger:.2f}") - logger.info(f"突破做多价(上1/3外): {long_breakout:.2f}, 突破做空价(下1/3外): {short_breakout:.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}") if skip_short_by_upper_third: - logger.info("上一根阴线+当前阳线(做多形态),不按上三分之一做空") + logger.info("上一根阴线+当前阳线(做多形态),不按上四分之一做空") if skip_long_by_lower_third: - logger.info("上一根阳线+当前阴线(做空形态),不按下三分之一做多") + logger.info("上一根阳线+当前阴线(做空形态),不按下四分之一做多") # 无持仓时检查开仓信号 if self.start == 0: if current_price >= long_breakout and not skip_long_by_lower_third: - logger.info(f"触发做多信号!价格 {current_price:.2f} >= 突破价(上1/3外) {long_breakout:.2f}") + logger.info(f"触发做多信号!价格 {current_price:.2f} >= 突破价(上1/4外) {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/3外) {short_breakout:.2f}") + logger.info(f"触发做空信号!价格 {current_price:.2f} <= 突破价(下1/4外) {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/3) {short_trigger:.2f}") + logger.info(f"持多反手做空!价格 {current_price:.2f} <= 触发价(上1/4) {short_trigger:.2f}") return ('reverse_short', short_trigger) # 反手条件2: 上一根K线上阴线涨幅>0.01%,当前跌到上一根实体下边 @@ -466,7 +339,7 @@ class BitmartFuturesTransaction: 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/3) {long_trigger:.2f}") + logger.info(f"持空反手做多!价格 {current_price:.2f} >= 触发价(下1/4) {long_trigger:.2f}") return ('reverse_long', long_trigger) # 反手条件2: 上一根K线下阴线跌幅>0.01%,当前涨到上一根实体上边 @@ -478,34 +351,6 @@ class BitmartFuturesTransaction: return None - def can_open(self, current_kline_id): - """开仓前过滤:同一根 K 线只开一次 + 开仓冷却时间。仅用于 long/short 新开仓。""" - now = time.time() - if self.last_open_kline_id is not None and self.last_open_kline_id == current_kline_id: - logger.info(f"开仓频率控制:本 K 线({current_kline_id})已开过仓,跳过") - return False - 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} 秒") - return False - return True - - def can_reverse(self, current_price, trigger_price): - """反手前过滤:冷却时间 + 最小价差""" - 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} 秒") - 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}%") - return False - - return True - def verify_no_position(self, max_retries=5, retry_interval=3): """ 验证当前无持仓 @@ -565,10 +410,6 @@ class BitmartFuturesTransaction: # 验证开仓是否成功 if self.verify_position_direction(1): - self.last_open_time = time.time() - self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None) - self.highest_since_entry = self.open_avg_price # ATR止盈:从开仓价开始追踪 - self.lowest_since_entry = None logger.success("开多成功") return True else: @@ -591,10 +432,6 @@ class BitmartFuturesTransaction: # 验证开仓是否成功 if self.verify_position_direction(-1): - self.last_open_time = time.time() - self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None) - self.lowest_since_entry = self.open_avg_price # ATR止盈:从开仓价开始追踪 - self.highest_since_entry = None logger.success("开空成功") return True else: @@ -619,10 +456,7 @@ class BitmartFuturesTransaction: time.sleep(3) if self.verify_position_direction(1): - self.highest_since_entry = self.open_avg_price # ATR止盈:从开仓价开始追踪 - self.lowest_since_entry = None logger.success("反手做多成功") - self.last_reverse_time = time.time() time.sleep(20) return True else: @@ -646,10 +480,7 @@ class BitmartFuturesTransaction: time.sleep(3) if self.verify_position_direction(-1): - self.lowest_since_entry = self.open_avg_price # ATR止盈:从开仓价开始追踪 - self.highest_since_entry = None logger.success("反手做空成功") - self.last_reverse_time = time.time() time.sleep(20) return True else: @@ -661,7 +492,7 @@ class BitmartFuturesTransaction: def action(self): """主循环""" - logger.info("开始运行三分之一策略交易...") + logger.info("开始运行四分之一策略交易...") # 启动时设置全仓高杠杆 if not self.set_leverage(): @@ -691,14 +522,12 @@ class BitmartFuturesTransaction: page_start = False try: - # 1. 获取完整K线数据(用于信号检测 + ATR计算) - all_klines = self.get_all_klines() - if len(all_klines) < 2: + # 1. 获取K线数据(当前K线和上一根K线) + prev_kline, current_kline = self.get_klines() + if not prev_kline or not current_kline: logger.warning("获取K线失败,等待重试...") time.sleep(5) continue - prev_kline = all_klines[-2] - current_kline = all_klines[-1] # 记录进入新的K线 current_kline_time = current_kline['id'] @@ -721,46 +550,10 @@ class BitmartFuturesTransaction: logger.debug(f"当前持仓状态: {self.start} (0=无, 1=多, -1=空)") - # 3.5 ATR 回调止盈检查(有持仓时) - if self.start != 0: - atr = self.calculate_atr(all_klines) - if self.check_trailing_stop(current_price, atr): - # 触发回调止盈,执行平仓 - self.平仓() - time.sleep(3) - # 轮询确认平仓成功(最多约10秒) - for _ in range(10): - if self.get_position_status() and self.start == 0: - break - time.sleep(1) - if self.start == 0: - logger.success("ATR回调止盈平仓成功") - self.highest_since_entry = None - self.lowest_since_entry = None - page_start = True - else: - logger.warning("ATR回调止盈平仓后仍有持仓,下次循环重试") - if page_start: - self.page.close() - time.sleep(5) - continue - # 4. 检查信号 signal = self.check_signal(current_price, prev_kline, current_kline) - # 5. 反手过滤:冷却时间 + 最小价差 - if signal and signal[0].startswith('reverse_'): - if not self.can_reverse(current_price, signal[1]): - signal = None - - # 5.5 开仓频率过滤:同一根 K 线只开一次 + 开仓冷却 - if signal and signal[0] in ('long', 'short'): - if not self.can_open(current_kline_time): - signal = None - else: - self._current_kline_id_for_open = current_kline_time # 供 execute_trade 成功后记录 - - # 6. 有信号则执行交易 + # 5. 有信号则执行交易 if signal: trade_success = self.execute_trade(signal) if trade_success: diff --git a/bitmart/四分之一策略说明-修改版.md b/bitmart/四分之一策略说明-修改版.md deleted file mode 100644 index cda56c7..0000000 --- a/bitmart/四分之一策略说明-修改版.md +++ /dev/null @@ -1,192 +0,0 @@ -# 四分之一策略 · ETHUSDT 5 分钟(修改版)— 详细说明 - -## 优化摘要(与初版对比) - -- **触发方式**:由「当前价与阈值比较」改为**穿越触发**(`cross_up` / `cross_down`),即上一轮价在阈值一侧、当前轮在另一侧才触发,避免“已在阈值下方/上方”的追单。 -- **实体过滤**:由固定 `entity < 0.1` 改为**百分比或 ATR**:`entity_filter_mode` 可选 `"pct"`(entity/close ≥ min_entity_pct)或 `"atr"`(entity ≥ entity_atr_ratio × ATR14)。 -- **影线反手**:上/下影线阈值由 0.01% 提高到 **0.05%**(可调),且影线反手也要求**穿越**实体边,减少频繁反手。 -- **EMA20**:拆成 **use_ema20_entry_filter**(开仓/反手方向过滤)与 **use_ema20_force_exit**(持仓强制平仓),解耦便于调参。 -- **平仓防针洗**:**exit_use_last_close = True** 时用已收盘 K 线的 `last_close` 判断 EMA 破位,减少瞬间插针触发平仓。 -- **ATR 追踪**:由「本 K 线内最高/最低」改为**自开仓以来**的 `_highest_since_entry` / `_lowest_since_entry`,换线不重置,真正追踪止盈。 -- **开盘延迟**:可选 **open_trigger_delay_seconds**(默认 0),当前 K 线开始后经过该秒数才允许触发,减轻开盘即触发。 - ---- - -## 一、策略概述 - -- **名称**:四分之一策略(Quarter Strategy) -- **标的**:Bitmart 合约 **ETHUSDT** -- **周期**:**5 分钟 K 线** -- **模式**:全仓(cross)、高杠杆(100x) -- **思路**:用「上一根 K 线实体」的 **1/4 位置** 做突破/回落触发,结合 EMA10/EMA20/ATR 止盈与趋势过滤,支持多空开仓与反手。 - ---- - -## 二、核心价格与 K 线定义 - -### 2.1 上一根 K 线实体 - -- **实体大小**:`entity = |close - open|`(绝对值) -- **实体上边**:`upper = max(open, close)` -- **实体下边**:`lower = min(open, close)` - -即:阳线时上边=收盘、下边=开盘;阴线时上边=开盘、下边=收盘。 - -### 2.2 四个关键价位(常规情况) - -以**上一根 K 线**的实体为基准: - -| 名称 | 计算公式 | 含义 | -|--------------|------------------------------|--------------------------| -| 做多触发 | `lower + entity/4` | 实体下方向上 1/4 处 | -| 做空触发 | `upper - entity/4` | 实体上边向下 1/4 处 | -| 突破做多 | `upper + entity/4` | 实体上边再向上 1/4 处 | -| 突破做空 | `lower - entity/4` | 实体下边再向下 1/4 处 | - -- **无仓时**:做多看「突破做多」价,做空看「做空触发」价。 -- **有仓时**:反手多/空用「做多触发」「做空触发」价。 - -### 2.3 跳空时的基准修正 - -当出现跳空时,用**当前 K 线开盘价**作为计算基准,使 1/4 区间对称于开盘价: - -- **情况 1**:上一根为**阳线**且**当前开盘价 > 上一根收盘价**(跳空高开) -- **情况 2**:上一根为**阴线**且**当前开盘价 < 上一根收盘价**(跳空低开) - -此时: - -- `calc_base = 当前 K 线 open` -- 做多触发 = `calc_base + entity/4` -- 做空触发 = `calc_base - entity/4` -- 突破做多 = `calc_base + entity/4`(与做多触发同) -- 突破做空 = `calc_base - entity/4`(与做空触发同) - -即跳空时以当前开盘为轴心,上下各 1/4 实体对称。 - ---- - -## 三、开仓与反手信号逻辑 - -### 3.1 前置过滤(所有信号共用) - -- **实体过小**:按 `entity_filter_mode` 二选一:`entity/close` 不低于 `min_entity_pct`(如 0.02%),或 `entity` 不低于 `entity_atr_ratio × ATR(14)`(如 0.2×ATR)。不满足则本根不产生信号。 -- **形态过滤**: - - **上一根阴线 + 当前阳线**(视为做多形态):不按「做空触发(上 1/4)」做空。 - - **上一根阳线 + 当前阴线**(视为做空形态):不按「做多触发(下 1/4)」做多。 -- **穿越触发**:所有开仓/反手均要求**上一轮价格在阈值一侧、当前轮穿越到另一侧**(避免追单)。 - -### 3.2 无持仓(start == 0) - -- **做多**:**穿越**突破做多(上一轮 < 突破价 ≤ 当前价),且未被做多形态过滤、且通过 EMA20 入场过滤 → 开多。 -- **做空**:**穿越**做空触发(上一轮 > 做空触发 ≥ 当前价),且未被做空形态过滤、且通过 EMA20 入场过滤 → 开空。 - -### 3.3 持多仓(start == 1)→ 反手做空 - -满足其一即反手空: - -1. **穿越**做空触发,且反手价差 ≥ reverse_min_move_pct,且 EMA20 入场过滤通过。 -2. **上影线反手**:上一根**上影线比例 > min_upper_shadow_pct**(如 0.05%),且**穿越**上一根实体下边。 - -### 3.4 持空仓(start == -1)→ 反手做多 - -满足其一即反手多: - -1. **穿越**做多触发,且反手价差 ≥ reverse_min_move_pct,且 EMA20 入场过滤通过。 -2. **下影线反手**:上一根**下影线比例 > min_lower_shadow_pct**(如 0.05%),且**穿越**上一根实体上边。 - ---- - -## 四、反手前的额外过滤 - -- **反手价差过滤**: - `reverse_min_move_pct = 0.05%` - 反手时要求:`|当前价 - 触发价| / 触发价 * 100 >= 0.05%`,否则不执行反手(避免刚触及就反手)。 - ---- - -## 五、开仓前的过滤 - -1. **同 K 线出场不再开仓**:若本根 K 线内已经因 EMA/ATR/EMA20 平过仓(`_last_exit_kline_id == current_kline_time`),本根 K 线内不再开多/开空,等下一根 K 线再判断。 -2. **EMA20 方向过滤**(`use_ema20_filter = True`): - - 开多 / 反手多:仅当 **当前价 > EMA20** 时允许。 - - 开空 / 反手空:仅当 **当前价 < EMA20** 时允许。 - 即逆势(价在 EMA20 另一侧)时暂停开仓/反手。 - ---- - -## 六、平仓(止盈/风控)规则 - -- **最短持仓时间**:`min_hold_seconds = 90` 秒。开仓或反手后,至少持仓 90 秒才允许下面「技术性平仓」。 -- **EMA/ATR 平仓开关**:`use_ema_atr_exit = True` 时,下面规则才生效。 -- **ATR 倍数**:`atr_multiplier = 1.1`,即 1.1 × ATR(14)。 -- **防针洗**:`exit_use_last_close = True` 时,EMA10/EMA20 破位用**已收盘 K 线的 last_close** 判断,减少插针触发平仓;ATR 仍用当前价与自开仓以来极值比较。 -- **EMA20 强制平**:由独立开关 **use_ema20_force_exit** 控制,与开仓用的 **use_ema20_entry_filter** 解耦。 - -### 6.1 多单平仓(满足其一即平) - -1. **EMA10 快出**:参考价 **< EMA10**(参考价 = last_close 或当前价,见上)→ 平多。 -2. **EMA20 强制平**(`use_ema20_force_exit = True`):参考价 **< EMA20** → 强制平多。 -3. **ATR 追踪止盈**:从**自开仓以来最高价**回撤 **≥ 1.1×ATR(14)**(用当前价)→ 平多。极值不随换线重置。 - -### 6.2 空单平仓(满足其一即平) - -1. **EMA10 快出**:参考价 **> EMA10** → 平空。 -2. **EMA20 强制平**:参考价 **> EMA20** → 强制平空。 -3. **ATR 追踪止盈**:从**自开仓以来最低价**反弹 **≥ 1.1×ATR(14)** → 平空。 - -### 6.3 指标计算说明 - -- **EMA10 / EMA20 / ATR(14) / last_close**:均基于「已收盘 K 线」计算(最近约 35 根 5m,排除当前未收盘一根);`get_ema_atr_for_exit` 返回 dict 含上述四项。 -- **自开仓以来最高/最低**:仅在持仓期间用当前价更新,用于 ATR 追踪;开仓/反手时重置为当次入场价,换线不重置。 - ---- - -## 七、交易执行与风控细节 - -- **下单方式**:市价单;开仓/反手统一张数 `default_order_size = 25`(可在代码中修改)。 -- **反手流程**:先市价平仓 → 轮询确认无持仓(最多约 10 秒)→ 再市价开新方向;反手成功后多等约 20 秒再继续循环,避免界面/接口延迟。 -- **开仓前校验**:开多/开空前会查持仓,若已有反向或同向持仓则放弃本次开仓,避免双向持仓。 -- **杠杆与账户**:启动时设置全仓、100x 杠杆;未使用 `risk_percent`,仓位由固定张数控制。 - ---- - -## 八、主循环流程概览 - -1. 拉取最近 2 根 5 分钟 K 线(上一根 + 当前根)及当前价。 -2. 每次循环通过 API 获取真实持仓状态(避免与交易所不同步)。 -3. **若已有持仓**: - 先判断是否满足「最短持仓 90 秒」+ EMA10/EMA20/ATR 平仓条件,满足则平仓并本 K 线内不再开仓。 -4. **信号检测**: - 根据当前价与上一根实体 1/4 计算做多/做空/突破价,结合形态过滤与持仓状态,得到开多、开空、反手多、反手空之一或无信号。 -5. **反手过滤**:若为反手信号,再检查反手价差 ≥ 0.05%。 -6. **开仓过滤**:若为开多/开空,检查「本 K 线未因技术性平仓」及 EMA20 方向过滤。 -7. **执行**:有信号则执行对应开仓或反手,成功后刷新页面/状态并短暂等待再进入下一轮。 - ---- - -## 九、关键参数汇总 - -| 参数 | 值 | 说明 | -|------------------------------|-----------|----------------------------------------| -| contract_symbol | ETHUSDT | 合约品种 | -| 周期 | 5 分钟 | K 线步长 | -| leverage | 100 | 杠杆倍数 | -| open_type | cross | 全仓 | -| default_order_size | 25 | 开仓/反手张数 | -| reverse_min_move_pct | 0.05 | 反手最小价差(%) | -| min_hold_seconds | 90 | 最短持仓时间(秒) | -| use_ema_atr_exit | True | 是否启用 EMA/ATR 平仓 | -| atr_multiplier | 1.1 | ATR 追踪止盈倍数(自开仓以来极值) | -| use_ema20_entry_filter | True | 开仓/反手方向过滤(价在 EMA20 同侧) | -| use_ema20_force_exit | True | 持仓强制平仓(价穿越 EMA20 则平) | -| exit_use_last_close | True | 用已收盘 last_close 判断 EMA 破位防针洗| -| entity_filter_mode | atr | 实体过滤:pct 或 atr | -| min_entity_pct | 0.02 | 实体/close ≥ 此%才交易(mode=pct) | -| entity_atr_ratio | 0.2 | 实体 ≥ 此倍数×ATR 才交易(mode=atr) | -| min_upper_shadow_pct | 0.05 | 上影线比例 > 此值才允许上影线反手 | -| min_lower_shadow_pct | 0.05 | 下影线比例 > 此值才允许下影线反手 | -| open_trigger_delay_seconds | 0 | 当前 K 线开始后延迟 N 秒再允许触发 | - ---- - -以上即为「四分之一,五分钟,反手条件充足修改版」中的完整策略逻辑说明;实际运行以代码为准,本文档便于理解和后续调参。