This commit is contained in:
薇薇安 2026-03-20 08:42:36 +08:00
parent 63e798f003
commit a38f7cf09d
3 changed files with 111 additions and 1 deletions

View File

@ -966,6 +966,13 @@ class ConfigManager:
'TREND_STATE_TTL_SEC': eff_get('TREND_STATE_TTL_SEC', 3600), 'TREND_STATE_TTL_SEC': eff_get('TREND_STATE_TTL_SEC', 3600),
'RECO_USE_TREND_ENTRY_FILTER': eff_get('RECO_USE_TREND_ENTRY_FILTER', True), 'RECO_USE_TREND_ENTRY_FILTER': eff_get('RECO_USE_TREND_ENTRY_FILTER', True),
'RECO_MAX_TREND_MOVE_BEFORE_ENTRY': eff_get('RECO_MAX_TREND_MOVE_BEFORE_ENTRY', 0.04), 'RECO_MAX_TREND_MOVE_BEFORE_ENTRY': eff_get('RECO_MAX_TREND_MOVE_BEFORE_ENTRY', 0.04),
# 回撤/区间入场(前低前高)
'ENTRY_PULLBACK_FILTER_ENABLED': eff_get('ENTRY_PULLBACK_FILTER_ENABLED', False),
'ENTRY_PULLBACK_INTERVAL': eff_get('ENTRY_PULLBACK_INTERVAL', None),
'ENTRY_PULLBACK_LOOKBACK_BARS': eff_get('ENTRY_PULLBACK_LOOKBACK_BARS', 24),
'ENTRY_PULLBACK_MIN_BARS': eff_get('ENTRY_PULLBACK_MIN_BARS', 5),
'ENTRY_PULLBACK_MAX_LONG_IN_RANGE': eff_get('ENTRY_PULLBACK_MAX_LONG_IN_RANGE', 0.62),
'ENTRY_PULLBACK_MIN_SHORT_IN_RANGE': eff_get('ENTRY_PULLBACK_MIN_SHORT_IN_RANGE', 0.38),
# 当前交易预设(让 trading_system 能知道是哪种模式) # 当前交易预设(让 trading_system 能知道是哪种模式)
'TRADING_PROFILE': profile, 'TRADING_PROFILE': profile,

View File

@ -363,6 +363,16 @@ DEFAULT_TRADING_CONFIG = {
# 推荐系统使用的最大允许趋势幅度(相对于趋势信号价),默认与自动交易相同或略微更严格 # 推荐系统使用的最大允许趋势幅度(相对于趋势信号价),默认与自动交易相同或略微更严格
'RECO_MAX_TREND_MOVE_BEFORE_ENTRY': 0.04, 'RECO_MAX_TREND_MOVE_BEFORE_ENTRY': 0.04,
# ===== 回撤/区间入场过滤参考近N根K线前低、前高减少买在区间顶部、卖在区间底部=====
# 与 USE_TREND_ENTRY_FILTER相对「信号价」的追价幅度互补本组用 K 线区间衡量是否已跑到局部极值附近。
'ENTRY_PULLBACK_FILTER_ENABLED': False, # True 启用;建议先 True 观察日志中 [回撤过滤] 跳过比例再调阈值
'ENTRY_PULLBACK_INTERVAL': None, # None=沿用 ENTRY_INTERVAL默认1h可改为 '15m' 等更敏感周期
'ENTRY_PULLBACK_LOOKBACK_BARS': 24, # 统计区间用的 K 线根数(需 >= MIN_BARS
'ENTRY_PULLBACK_MIN_BARS': 5, # 至少多少根有效 K 才启用过滤,不足则跳过本过滤
# 现价在 [区间低,区间高] 中的相对位置 pos∈[0,1]0=贴前低1=贴前高
'ENTRY_PULLBACK_MAX_LONG_IN_RANGE': 0.62, # 做多pos 超过此值则不开(默认拒绝最上约 38% 区域)
'ENTRY_PULLBACK_MIN_SHORT_IN_RANGE': 0.38, # 做空pos 低于此值则不开(默认拒绝最下约 38% 区域)
# ===== 智能入场方案C===== # ===== 智能入场方案C=====
# 根治方案:默认关闭。关闭后回归“纯限价单模式”(不追价/不市价兜底/未成交撤单跳过) # 根治方案:默认关闭。关闭后回归“纯限价单模式”(不追价/不市价兜底/未成交撤单跳过)
'SMART_ENTRY_ENABLED': True, # 开启智能入场,提高成交率 'SMART_ENTRY_ENABLED': True, # 开启智能入场,提高成交率

View File

@ -8,7 +8,7 @@ import random
import time import time
import aiohttp import aiohttp
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional from typing import Dict, List, Optional, Tuple
from datetime import datetime from datetime import datetime
try: try:
from .binance_client import BinanceClient, AlgoOrderPositionUnavailableError from .binance_client import BinanceClient, AlgoOrderPositionUnavailableError
@ -149,6 +149,46 @@ except Exception:
get_balance_from_cache = lambda: None get_balance_from_cache = lambda: None
def _pullback_range_metrics_from_klines(
klines: Optional[List],
current_price: float,
) -> Optional[Tuple[float, float, float]]:
"""
用近若干根 K 线的最高价/最低价构成区间衡量当前价在区间内的相对位置
返回 (recent_low, recent_high, position_01)其中 position_01[0,1]
- 0 表示贴在区间下沿接近前低一侧
- 1 表示贴在区间上沿接近前高一侧
多单若 position_01 过大说明价格已跑到区间顶部容易一回撤就扫损
空单若 position_01 过小同理接近区间下沿追空风险大
"""
if not klines or current_price is None:
return None
try:
cur = float(current_price)
except (TypeError, ValueError):
return None
if cur <= 0:
return None
highs: List[float] = []
lows: List[float] = []
for k in klines:
try:
highs.append(float(k[2]))
lows.append(float(k[3]))
except (IndexError, TypeError, ValueError):
continue
if len(highs) < 2:
return None
hi = max(highs)
lo = min(lows)
if hi <= lo:
return None
pos = (cur - lo) / (hi - lo)
return (lo, hi, pos)
class PositionManager: class PositionManager:
"""仓位管理类""" """仓位管理类"""
@ -425,6 +465,59 @@ class PositionManager:
except Exception as e: except Exception as e:
logger.debug(f"{symbol} 趋势入场过滤时出错(忽略,按正常逻辑继续): {e}") logger.debug(f"{symbol} 趋势入场过滤时出错(忽略,按正常逻辑继续): {e}")
# ===== 回撤/区间入场过滤(参考近 N 根 K 线的前低/前高,避免买在区间顶部、卖在区间底部)=====
try:
pb_enabled = bool(config.TRADING_CONFIG.get("ENTRY_PULLBACK_FILTER_ENABLED", False))
if pb_enabled:
min_bars = max(3, int(config.TRADING_CONFIG.get("ENTRY_PULLBACK_MIN_BARS", 5) or 5))
lookback = max(min_bars, int(config.TRADING_CONFIG.get("ENTRY_PULLBACK_LOOKBACK_BARS", 24) or 24))
interval_pb = config.TRADING_CONFIG.get("ENTRY_PULLBACK_INTERVAL") or config.TRADING_CONFIG.get(
"ENTRY_INTERVAL", "1h"
)
max_long_pos = float(config.TRADING_CONFIG.get("ENTRY_PULLBACK_MAX_LONG_IN_RANGE", 0.62) or 0.62)
min_short_pos = float(config.TRADING_CONFIG.get("ENTRY_PULLBACK_MIN_SHORT_IN_RANGE", 0.38) or 0.38)
max_long_pos = min(1.0, max(0.0, max_long_pos))
min_short_pos = min(1.0, max(0.0, min_short_pos))
if min_short_pos > max_long_pos:
min_short_pos, max_long_pos = max_long_pos, min_short_pos
rt = None
try:
rt = self.client.get_realtime_price(symbol)
except Exception:
rt = None
cur_pb = float(rt or estimated_entry_price)
bars_pb = klines if klines and len(klines) >= min_bars else None
if not bars_pb:
try:
fetched = await self.client.get_klines(
symbol=symbol, interval=str(interval_pb), limit=lookback
)
bars_pb = fetched if fetched and len(fetched) >= min_bars else None
except Exception as e:
logger.debug(f"{symbol} 回撤过滤拉取K线失败跳过该过滤: {e}")
bars_pb = None
metrics = _pullback_range_metrics_from_klines(bars_pb, cur_pb)
if metrics is not None:
lo_pb, hi_pb, pos_pb = metrics
es = estimated_side.upper()
if es == "BUY" and pos_pb > max_long_pos:
logger.info(
f"{symbol} [回撤过滤] 做多跳过:现价={cur_pb:.6f} 在近{len(bars_pb)}{interval_pb}区间"
f"[{lo_pb:.6f},{hi_pb:.6f}] 中位置={pos_pb:.2f} > 上限{max_long_pos:.2f}(接近区间上沿/前高,易回撤扫损)"
)
return None
if es == "SELL" and pos_pb < min_short_pos:
logger.info(
f"{symbol} [回撤过滤] 做空跳过:现价={cur_pb:.6f} 在近{len(bars_pb)}{interval_pb}区间"
f"[{lo_pb:.6f},{hi_pb:.6f}] 中位置={pos_pb:.2f} < 下限{min_short_pos:.2f}(接近区间下沿/前低,易反弹扫损)"
)
return None
except Exception as e:
logger.debug(f"{symbol} 回撤/区间入场过滤异常(忽略,继续开仓流程): {e}")
# 估算止损价格(用于固定风险计算) # 估算止损价格(用于固定风险计算)
estimated_stop_loss = None estimated_stop_loss = None
if atr and atr > 0: if atr and atr > 0: