优化回调平仓规则

This commit is contained in:
ddrwode
2026-02-06 15:48:44 +08:00
parent b979f8ff42
commit aaa3a655bb

View File

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