a
This commit is contained in:
parent
3865e25a2b
commit
8eb2476192
|
|
@ -253,6 +253,17 @@ def _get_trading_config():
|
|||
# 是否允许 4H 趋势为 neutral 时自动交易;默认不允许(震荡最易扫损)
|
||||
'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)=====
|
||||
# 根治方案:默认关闭。关闭后回归“纯限价单模式”(不追价/不市价兜底/未成交撤单跳过)
|
||||
'SMART_ENTRY_ENABLED': True, # 开启智能入场,提高成交率
|
||||
|
|
|
|||
|
|
@ -451,6 +451,39 @@ class MarketScanner:
|
|||
# 强度上限归一到 0-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 {
|
||||
'symbol': symbol,
|
||||
# 技术分析使用的价格(K线收盘价)
|
||||
|
|
|
|||
|
|
@ -208,6 +208,55 @@ class PositionManager:
|
|||
estimated_entry_price = ticker['price']
|
||||
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
|
||||
if atr and atr > 0:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user