优化完美代码,优化计算盈亏

This commit is contained in:
27942
2026-02-08 15:54:31 +08:00
parent 4ed52e6e93
commit bfcc6161b6
2 changed files with 22 additions and 421 deletions

View File

@@ -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:

View File

@@ -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 秒再允许触发 |
---
以上即为「四分之一,五分钟,反手条件充足修改版」中的完整策略逻辑说明;实际运行以代码为准,本文档便于理解和后续调参。