This commit is contained in:
薇薇安 2026-01-28 17:12:45 +08:00
parent 3865e25a2b
commit 8eb2476192
3 changed files with 97 additions and 4 deletions

View File

@ -253,6 +253,17 @@ def _get_trading_config():
# 是否允许 4H 趋势为 neutral 时自动交易;默认不允许(震荡最易扫损) # 是否允许 4H 趋势为 neutral 时自动交易;默认不允许(震荡最易扫损)
'AUTO_TRADE_ALLOW_4H_NEUTRAL': False, 'AUTO_TRADE_ALLOW_4H_NEUTRAL': False,
# ===== 趋势入场过滤(防止追在半山腰)=====
# 是否启用基于趋势状态的入场过滤:
# - 扫描阶段会为每个强信号交易对写入一份“趋势状态”到缓存Redis
# - 开仓时根据当前价格相对于信号价格的偏移,过滤“过晚追价”的入场
'USE_TREND_ENTRY_FILTER': True,
# 在信号方向上允许的最大累计趋势幅度(相对于信号价),超过则认为“时机太晚”,不再入场
# 例如0.05 表示价格沿趋势方向已经走了 5% 以上还没上车,则跳过本轮机会
'MAX_TREND_MOVE_BEFORE_ENTRY': 0.05,
# 趋势状态缓存的 TTL用于控制一轮趋势的“有效期”
'TREND_STATE_TTL_SEC': 3600,
# ===== 智能入场方案C===== # ===== 智能入场方案C=====
# 根治方案:默认关闭。关闭后回归“纯限价单模式”(不追价/不市价兜底/未成交撤单跳过) # 根治方案:默认关闭。关闭后回归“纯限价单模式”(不追价/不市价兜底/未成交撤单跳过)
'SMART_ENTRY_ENABLED': True, # 开启智能入场,提高成交率 'SMART_ENTRY_ENABLED': True, # 开启智能入场,提高成交率

View File

@ -451,6 +451,39 @@ class MarketScanner:
# 强度上限归一到 0-10 # 强度上限归一到 0-10
signal_strength = max(0, min(int(signal_strength), 10)) signal_strength = max(0, min(int(signal_strength), 10))
# ===== 趋势状态缓存(用于后续“入场时机过滤”)=====
try:
# 只有在方向明确且信号强度达到最小门槛时,才记录趋势状态
min_strength = int(config.TRADING_CONFIG.get('MIN_SIGNAL_STRENGTH', 7) or 7)
use_trend_filter = bool(config.TRADING_CONFIG.get('USE_TREND_ENTRY_FILTER', False))
if use_trend_filter and direction and signal_strength >= min_strength:
trend_state_key = f"trend_state:{symbol}"
trend_state_value = {
'symbol': symbol,
'direction': direction, # BUY / SELL
'signal_strength': int(signal_strength),
'marketRegime': market_regime,
'trend_4h': trend_4h,
# 使用技术分析用的收盘价作为“信号价格”,同时附带 24h ticker 价格
'signal_price': float(current_price),
'ticker_price': float(ticker_price) if ticker_price is not None else None,
'ema20': float(ema20) if ema20 is not None else None,
'ema50': float(ema50) if ema50 is not None else None,
'ema20_4h': float(ema20_4h) if ema20_4h is not None else None,
'price_4h': float(price_4h) if price_4h is not None else None,
'last_kline_time': last_kline_time,
'created_at_ts': int(ticker_ts or 0),
}
ttl_sec = int(config.TRADING_CONFIG.get('TREND_STATE_TTL_SEC', 3600) or 3600)
if self.client and getattr(self.client, "redis_cache", None):
try:
await self.client.redis_cache.set(trend_state_key, trend_state_value, ttl=ttl_sec)
logger.debug(f"{symbol} 趋势状态已缓存: dir={direction}, strength={signal_strength}, price={current_price:.6f}")
except Exception as e:
logger.debug(f"{symbol} 缓存趋势状态失败: {e}")
except Exception as e:
logger.debug(f"{symbol} 处理趋势状态缓存时出错: {e}")
return { return {
'symbol': symbol, 'symbol': symbol,
# 技术分析使用的价格K线收盘价 # 技术分析使用的价格K线收盘价

View File

@ -208,6 +208,55 @@ class PositionManager:
estimated_entry_price = ticker['price'] estimated_entry_price = ticker['price']
estimated_side = trade_direction if trade_direction else ('BUY' if change_percent > 0 else 'SELL') estimated_side = trade_direction if trade_direction else ('BUY' if change_percent > 0 else 'SELL')
# ===== 趋势入场过滤(防止在趋势尾部追价)=====
try:
use_trend_filter = bool(config.TRADING_CONFIG.get("USE_TREND_ENTRY_FILTER", False))
if use_trend_filter and getattr(self.client, "redis_cache", None):
trend_state_key = f"trend_state:{symbol}"
trend_state = await self.client.redis_cache.get(trend_state_key)
if trend_state:
trend_dir = (trend_state.get("direction") or "").upper()
signal_price = float(trend_state.get("signal_price") or 0) or None
if signal_price and trend_dir in ("BUY", "SELL"):
# 使用更实时的价格(如有),否则使用估算价
realtime_price = None
try:
realtime_price = self.client.get_realtime_price(symbol)
except Exception:
realtime_price = None
current_price = float(realtime_price or estimated_entry_price)
max_move = float(config.TRADING_CONFIG.get("MAX_TREND_MOVE_BEFORE_ENTRY", 0.05) or 0.05)
too_late = False
move_pct = 0.0
if trend_dir == "BUY":
# 做多:如果当前价相比信号价已经上涨超过阈值,认为追在高位
if current_price > signal_price:
move_pct = (current_price - signal_price) / signal_price
if move_pct > max_move:
too_late = True
elif trend_dir == "SELL":
# 做空:如果当前价相比信号价已经下跌超过阈值,认为追在低位
if current_price < signal_price:
move_pct = (signal_price - current_price) / signal_price
if move_pct > max_move:
too_late = True
# 只有当当前入场方向与趋势方向一致时,才应用“太晚不追”规则
if estimated_side.upper() == trend_dir and too_late:
logger.info(
f"{symbol} [入场过滤] 趋势方向={trend_dir}, 信号价={signal_price:.6f}, "
f"当前价={current_price:.6f}, 累计趋势幅度={move_pct*100:.2f}%>允许上限={max_move*100:.2f}%"
f"认为本轮趋势入场时机已错过,跳过自动开仓"
)
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: