1
This commit is contained in:
parent
dba6e569c6
commit
6ac175038e
@ -109,7 +109,9 @@ class Settings(BaseSettings):
|
||||
|
||||
# 加密货币交易智能体配置
|
||||
crypto_symbols: str = "BTCUSDT,ETHUSDT" # 监控的交易对,逗号分隔
|
||||
crypto_llm_threshold: float = 0.70 # 触发 LLM 分析的置信度阈值
|
||||
crypto_llm_threshold: float = 0.70 # 旧的统一阈值,兼容保留
|
||||
crypto_intraday_signal_threshold: float = 0.68 # 日内信号置信度阈值
|
||||
crypto_trend_signal_threshold: float = 0.72 # 趋势信号置信度阈值
|
||||
|
||||
# 价格监控模式配置
|
||||
use_bitget_websocket: bool = True # 是否使用 Bitget WebSocket 实时价格(默认 False 使用 Binance 轮询)
|
||||
@ -119,14 +121,14 @@ class Settings(BaseSettings):
|
||||
crypto_min_volatility_percent: float = 0.5 # 最小波动率(百分比),低于此值跳过分析
|
||||
crypto_min_price_range_percent: float = 0.3 # 最小价格变动范围(百分比),低于此值跳过分析
|
||||
crypto_5m_surge_threshold: float = 1.0 # 5分钟突发波动阈值(百分比),超过此值即使1小时波动率低也会触发分析
|
||||
crypto_intraday_llm_cooldown_minutes: int = 15 # 日内 LLM 分析冷却时间
|
||||
crypto_trend_llm_cooldown_minutes: int = 60 # 趋势 LLM 分析冷却时间
|
||||
crypto_intraday_llm_cooldown_minutes: int = 8 # 日内 LLM 分析冷却时间
|
||||
crypto_trend_llm_cooldown_minutes: int = 25 # 趋势 LLM 分析冷却时间
|
||||
crypto_force_llm_surge_threshold: float = 1.2 # 15分钟突发波动强制触发 LLM 的阈值
|
||||
crypto_force_llm_trade_zone_pct: float = 0.25 # 接近关键交易区时强制触发 LLM 的距离阈值
|
||||
crypto_event_analysis_enabled: bool = True # 是否启用实时行情事件触发分析
|
||||
crypto_event_analysis_window_minutes: int = 5 # 实时行情异动检测窗口
|
||||
crypto_event_analysis_price_change_percent: float = 0.8 # 检测窗口内涨跌超过该阈值触发日内分析
|
||||
crypto_event_analysis_cooldown_minutes: int = 10 # 同一交易对事件触发分析冷却
|
||||
crypto_event_analysis_cooldown_minutes: int = 5 # 同一交易对事件触发分析冷却
|
||||
|
||||
# Brave Search API 配置
|
||||
brave_api_key: str = ""
|
||||
|
||||
@ -503,7 +503,8 @@ class CryptoAgent:
|
||||
last_symbol = self._analysis_monitor.get("last_analysis_symbol") or "-"
|
||||
last_status = self._analysis_monitor.get("last_analysis_status") or "unknown"
|
||||
last_detail = self._analysis_monitor.get("last_analysis_detail") or "最近一轮分析已完成"
|
||||
threshold = self.settings.crypto_llm_threshold * 100
|
||||
intraday_threshold = self._get_signal_threshold_pct(signal_type='short_term')
|
||||
trend_threshold = self._get_signal_threshold_pct(signal_type='medium_term')
|
||||
|
||||
title = "💓 [分析心跳] 系统运行正常"
|
||||
content = "\n".join([
|
||||
@ -513,7 +514,7 @@ class CryptoAgent:
|
||||
f"**完成分析**: {symbol_completed} 个交易对",
|
||||
f"**跳过分析**: {symbol_skipped} 次",
|
||||
f"**分析异常**: {symbol_errors} 次",
|
||||
f"**信号阈值**: {threshold:.0f}%",
|
||||
f"**信号阈值**: 日内 {intraday_threshold:.0f}% / 趋势 {trend_threshold:.0f}%",
|
||||
f"**最近分析对象**: {last_symbol}",
|
||||
f"**最近状态**: {last_status}",
|
||||
f"**最近说明**: {last_detail}",
|
||||
@ -615,20 +616,9 @@ class CryptoAgent:
|
||||
return False, ""
|
||||
|
||||
def _resolve_llm_lanes_for_symbol(self, symbol: str, data: Dict[str, pd.DataFrame]) -> tuple[List[str], Dict[str, Any], str]:
|
||||
now = datetime.now()
|
||||
state = self._get_lane_state(symbol)
|
||||
force, force_reason = self._detect_force_llm_trigger(symbol, data)
|
||||
lanes: List[str] = []
|
||||
|
||||
intraday_last = self._parse_iso_datetime(state.get("last_intraday_at"))
|
||||
trend_last = self._parse_iso_datetime(state.get("last_trend_at"))
|
||||
intraday_cooldown = timedelta(minutes=self.settings.crypto_intraday_llm_cooldown_minutes)
|
||||
trend_cooldown = timedelta(minutes=self.settings.crypto_trend_llm_cooldown_minutes)
|
||||
|
||||
if force or intraday_last is None or not state.get("cached_intraday") or now - intraday_last >= intraday_cooldown:
|
||||
lanes.append("intraday")
|
||||
if force or trend_last is None or not state.get("cached_trend") or now - trend_last >= trend_cooldown:
|
||||
lanes.append("trend")
|
||||
lanes: List[str] = ["intraday", "trend"]
|
||||
|
||||
cached_results = {}
|
||||
if state.get("cached_intraday"):
|
||||
@ -636,9 +626,6 @@ class CryptoAgent:
|
||||
if state.get("cached_trend"):
|
||||
cached_results["trend"] = state["cached_trend"]
|
||||
|
||||
if not lanes and not cached_results:
|
||||
lanes = ["intraday", "trend"]
|
||||
|
||||
state["last_force_reason"] = force_reason if force else ""
|
||||
return lanes, cached_results, force_reason
|
||||
|
||||
@ -1245,7 +1232,7 @@ class CryptoAgent:
|
||||
f"📊 **监控交易对**: {len(self.symbols)} 个",
|
||||
f" {', '.join(self.symbols)}",
|
||||
f"⏰ **运行频率**: 每5分钟轻扫描",
|
||||
f"🧊 **LLM 冷却**: 日内 {self.settings.crypto_intraday_llm_cooldown_minutes} 分钟 / 趋势 {self.settings.crypto_trend_llm_cooldown_minutes} 分钟",
|
||||
f"🧠 **LLM 执行**: 每轮双 lane 直接分析,不做冷却缓存",
|
||||
f"💰 **交易系统**: 已启用(后台统一监控)",
|
||||
f"🎯 **分析维度**: 技术面 + 资金面 + 情绪面",
|
||||
]
|
||||
@ -1497,25 +1484,6 @@ class CryptoAgent:
|
||||
price_change_24h = self._calculate_price_change(data['1h'])
|
||||
logger.info(f"💰 当前价格: ${current_price:,.2f} ({price_change_24h})")
|
||||
|
||||
paper_symbol_cooldown = None
|
||||
if self.settings.paper_trading_enabled:
|
||||
paper_symbol_cooldown = self._refresh_symbol_trade_cooldown('PaperTrading', symbol)
|
||||
if paper_symbol_cooldown.get('should_cool_down'):
|
||||
cooldown_until = paper_symbol_cooldown.get('cooldown_until')
|
||||
cooldown_text = cooldown_until.isoformat() if cooldown_until else ''
|
||||
self._record_analysis_event(
|
||||
"symbol_trade_cooldown",
|
||||
symbol=symbol,
|
||||
status="warning",
|
||||
detail=paper_symbol_cooldown.get('reason', '交易对处于冷却中'),
|
||||
extra={
|
||||
"platform": "PaperTrading",
|
||||
"losing_streak": paper_symbol_cooldown.get('losing_streak', 0),
|
||||
"cooldown_until": cooldown_text,
|
||||
},
|
||||
)
|
||||
logger.info(f"⏸️ 模拟盘交易对冷却: {paper_symbol_cooldown.get('reason')} | until={cooldown_text}")
|
||||
|
||||
# 1.5. 波动率检查(节省 LLM 调用)
|
||||
should_analyze, volatility_reason, volatility = self._check_volatility(symbol, data)
|
||||
if not should_analyze:
|
||||
@ -1545,24 +1513,19 @@ class CryptoAgent:
|
||||
logger.info(f"\n🤖 【第一阶段:市场信号分析】 lanes={lanes_to_run or ['cache_only']}")
|
||||
if force_reason:
|
||||
logger.info(f" ⚡ 强制触发 LLM: {force_reason}")
|
||||
elif not lanes_to_run:
|
||||
logger.info(" 🧊 LLM 冷却中,使用上一轮 lane 缓存结果")
|
||||
|
||||
if lanes_to_run:
|
||||
self._bump_analysis_stat("llm_analyses")
|
||||
for lane in lanes_to_run:
|
||||
self._bump_lane_call(lane)
|
||||
else:
|
||||
self._bump_analysis_stat("cache_only_runs")
|
||||
self._bump_analysis_stat("llm_analyses")
|
||||
for lane in lanes_to_run:
|
||||
self._bump_lane_call(lane)
|
||||
|
||||
self._record_analysis_event(
|
||||
"llm_lane_plan",
|
||||
symbol=symbol,
|
||||
status="info",
|
||||
detail=force_reason or (f"本轮执行 lane: {', '.join(lanes_to_run)}" if lanes_to_run else "LLM 冷却中,使用缓存 lane 结果"),
|
||||
detail=force_reason or f"本轮执行 lane: {', '.join(lanes_to_run)}",
|
||||
extra={
|
||||
"lanes_to_run": lanes_to_run,
|
||||
"cache_only": not bool(lanes_to_run),
|
||||
"cache_only": False,
|
||||
"force_reason": force_reason,
|
||||
"trigger_source": trigger_source,
|
||||
},
|
||||
@ -1627,19 +1590,19 @@ class CryptoAgent:
|
||||
return
|
||||
|
||||
# 检查是否有达到阈值的交易信号
|
||||
threshold = self.settings.crypto_llm_threshold * 100 # 转换为百分比
|
||||
valid_signals = [s for s in trade_signals if s.get('confidence', 0) >= threshold]
|
||||
valid_signals = self._filter_valid_trade_signals(trade_signals)
|
||||
|
||||
if not valid_signals:
|
||||
self._bump_analysis_stat("threshold_filtered_runs")
|
||||
self._analysis_monitor["last_analysis_completed_at"] = datetime.now().isoformat()
|
||||
self._analysis_monitor["last_analysis_status"] = "completed"
|
||||
self._analysis_monitor["last_analysis_detail"] = f"完成分析,但无信号达到阈值 {threshold}%"
|
||||
thresholds_text = f"日内 {self._get_signal_threshold_pct(signal_type='short_term'):.0f}% / 趋势 {self._get_signal_threshold_pct(signal_type='medium_term'):.0f}%"
|
||||
self._analysis_monitor["last_analysis_detail"] = f"完成分析,但无信号达到阈值 {thresholds_text}"
|
||||
self._record_analysis_event(
|
||||
"symbol_analysis_completed",
|
||||
symbol=symbol,
|
||||
status="success",
|
||||
detail=f"完成分析,但无信号达到阈值 {threshold}%",
|
||||
detail=f"完成分析,但无信号达到阈值 {thresholds_text}",
|
||||
extra={
|
||||
"trade_signals": len(trade_signals),
|
||||
"valid_signals": 0,
|
||||
@ -1648,12 +1611,12 @@ class CryptoAgent:
|
||||
"post_regime_lane_signal_counts": market_signal.get("post_regime_lane_signal_counts") or {},
|
||||
},
|
||||
)
|
||||
logger.info(f"\n⏸️ 结论: 无交易信号达到置信度阈值 ({threshold}%),继续观望")
|
||||
logger.info(f"\n⏸️ 结论: 无交易信号达到分周期阈值(日内 {self._get_signal_threshold_pct(signal_type='short_term'):.0f}% / 趋势 {self._get_signal_threshold_pct(signal_type='medium_term'):.0f}%),继续观望")
|
||||
return
|
||||
|
||||
self._bump_analysis_stat("valid_signal_runs")
|
||||
self._bump_analysis_stat("valid_signals_total", len(valid_signals))
|
||||
logger.info(f"\n✅ 发现 {len(valid_signals)} 个有效交易信号(达到 {threshold}% 阈值)")
|
||||
logger.info(f"\n✅ 发现 {len(valid_signals)} 个有效交易信号(按周期阈值过滤)")
|
||||
for signal in valid_signals:
|
||||
logger.info(
|
||||
f" - {signal.get('timeframe', signal.get('type', 'unknown'))} | "
|
||||
@ -2036,18 +1999,34 @@ class CryptoAgent:
|
||||
profile['setup_type'] = setup_type
|
||||
return profile
|
||||
|
||||
def _get_signal_threshold_pct(self, signal: Optional[Dict[str, Any]] = None, signal_type: Optional[str] = None) -> float:
|
||||
resolved_type = signal_type or (signal or {}).get('timeframe') or (signal or {}).get('type') or 'medium_term'
|
||||
if resolved_type == 'short_term':
|
||||
threshold = getattr(self.settings, 'crypto_intraday_signal_threshold', None)
|
||||
elif resolved_type == 'medium_term':
|
||||
threshold = getattr(self.settings, 'crypto_trend_signal_threshold', None)
|
||||
else:
|
||||
threshold = None
|
||||
|
||||
if threshold is None:
|
||||
threshold = getattr(self.settings, 'crypto_llm_threshold', 0.70)
|
||||
return float(threshold) * 100
|
||||
|
||||
def _filter_valid_trade_signals(self, signals: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
return [
|
||||
signal for signal in (signals or [])
|
||||
if signal.get('action') in {'buy', 'sell'}
|
||||
and float(signal.get('confidence', 0) or 0) >= self._get_signal_threshold_pct(signal=signal)
|
||||
]
|
||||
|
||||
async def _execute_decisions(self, paper_decision: Dict[str, Any],
|
||||
bitget_decisions: Dict[str, Dict[str, Any]],
|
||||
market_signal: Dict[str, Any], current_price: float):
|
||||
"""执行交易决策(模拟盘 + Bitget 独立)"""
|
||||
# 保存本轮所有达到阈值的可交易信号,避免分流后只落一条信号
|
||||
threshold = self.settings.crypto_llm_threshold * 100
|
||||
symbol = market_signal.get('symbol')
|
||||
signals = market_signal.get('signals', [])
|
||||
valid_signals = [
|
||||
signal for signal in signals
|
||||
if signal.get('action') in {'buy', 'sell'} and signal.get('confidence', 0) >= threshold
|
||||
]
|
||||
valid_signals = self._filter_valid_trade_signals(signals)
|
||||
|
||||
for signal in valid_signals:
|
||||
signal_to_save = signal.copy()
|
||||
@ -2673,12 +2652,8 @@ class CryptoAgent:
|
||||
current_price: float):
|
||||
"""发送市场信号通知(第一阶段)- 调用前已确保有有效信号"""
|
||||
try:
|
||||
# 获取配置的阈值
|
||||
threshold = self.settings.crypto_llm_threshold * 100 # 转换为百分比
|
||||
|
||||
# 过滤达到阈值的信号(防御性检查)
|
||||
signals = market_signal.get('signals', [])
|
||||
valid_signals = [s for s in signals if s.get('confidence', 0) >= threshold]
|
||||
valid_signals = self._filter_valid_trade_signals(signals)
|
||||
|
||||
if not valid_signals:
|
||||
return
|
||||
@ -4017,12 +3992,6 @@ class CryptoAgent:
|
||||
blocked_reasons = regime_profile.get('no_trade_reasons') or ['当前市场状态不允许交易']
|
||||
return False, f"市场状态过滤: {';'.join(blocked_reasons[:2])}"
|
||||
|
||||
symbol_cooldown = self._get_symbol_trade_cooldown(platform_name, signal.get('symbol', ''))
|
||||
if symbol_cooldown and symbol_cooldown.get('should_cool_down'):
|
||||
cooldown_until = symbol_cooldown.get('cooldown_until')
|
||||
until_text = cooldown_until.isoformat() if cooldown_until else ''
|
||||
return False, f"交易对冷却中: {symbol_cooldown.get('reason', '').strip()} {until_text}".strip()
|
||||
|
||||
setup_passed, setup_reason = self._check_setup_execution_constraints(signal)
|
||||
if not setup_passed:
|
||||
return False, f"setup 过滤: {setup_reason}"
|
||||
@ -4095,12 +4064,6 @@ class CryptoAgent:
|
||||
elif signal_action == 'sell' and fr_pct < -0.05:
|
||||
return False, f"资金费率过冷 {fr_pct:.4f}%(做空拥挤),拒绝做空"
|
||||
|
||||
# 6. 连败降温检查(不拒绝,但降低仓位)
|
||||
streak_info = self._check_losing_streak(platform_name)
|
||||
if streak_info.get('should_cool_down'):
|
||||
signal['_streak_margin_multiplier'] = streak_info['margin_multiplier']
|
||||
logger.warning(f"[{platform_name}] ⚠️ {streak_info['reason']}")
|
||||
|
||||
return True, "通过风控检查"
|
||||
|
||||
def execute_signal_with_rules(self, signal: Dict[str, Any],
|
||||
@ -4440,20 +4403,10 @@ class CryptoAgent:
|
||||
return False
|
||||
|
||||
confidence = signal.get('confidence', 0)
|
||||
# 使用配置文件中的阈值
|
||||
threshold = self.settings.crypto_llm_threshold * 100 # 转换为百分比
|
||||
threshold = self._get_signal_threshold_pct(signal=signal)
|
||||
if confidence < threshold:
|
||||
return False
|
||||
|
||||
# 检查冷却时间(30分钟内不重复发送相同方向的信号)
|
||||
if symbol in self.signal_cooldown:
|
||||
cooldown_end = self.signal_cooldown[symbol] + timedelta(minutes=30)
|
||||
if datetime.now() < cooldown_end:
|
||||
if symbol in self.last_signals:
|
||||
if self.last_signals[symbol].get('action') == action:
|
||||
logger.debug(f"{symbol} 信号冷却中,跳过")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
async def _review_and_adjust_positions(
|
||||
@ -4560,11 +4513,7 @@ class CryptoAgent:
|
||||
)
|
||||
|
||||
signals = market_signal.get('signals', [])
|
||||
threshold = self.settings.crypto_llm_threshold * 100
|
||||
valid_signals = [
|
||||
signal for signal in signals
|
||||
if signal.get('action') in {'buy', 'sell'} and signal.get('confidence', 0) >= threshold
|
||||
]
|
||||
valid_signals = self._filter_valid_trade_signals(signals)
|
||||
|
||||
execution_preview: Dict[str, Any] = {}
|
||||
|
||||
@ -4627,8 +4576,10 @@ class CryptoAgent:
|
||||
'execution_guardian': self.execution_guardian.get_status(),
|
||||
'llm_schedule': {
|
||||
'scan_interval_minutes': 5,
|
||||
'intraday_cooldown_minutes': self.settings.crypto_intraday_llm_cooldown_minutes,
|
||||
'trend_cooldown_minutes': self.settings.crypto_trend_llm_cooldown_minutes,
|
||||
'intraday_cooldown_minutes': 0,
|
||||
'trend_cooldown_minutes': 0,
|
||||
'intraday_signal_threshold': self._get_signal_threshold_pct(signal_type='short_term'),
|
||||
'trend_signal_threshold': self._get_signal_threshold_pct(signal_type='medium_term'),
|
||||
'force_surge_threshold': self.settings.crypto_force_llm_surge_threshold,
|
||||
'force_trade_zone_pct': self.settings.crypto_force_llm_trade_zone_pct,
|
||||
'event_analysis_enabled': self.settings.crypto_event_analysis_enabled,
|
||||
|
||||
@ -90,15 +90,15 @@ class RegimeEngine:
|
||||
{
|
||||
"regime_key": "transition",
|
||||
"market_state_label": "过渡市",
|
||||
"tradability": "avoid",
|
||||
"tradability": "selective",
|
||||
"risk_mode": "defensive",
|
||||
"allowed_lanes": [],
|
||||
"preferred_lanes": [],
|
||||
"allowed_setups": [],
|
||||
"preferred_entry_types": [],
|
||||
"allowed_lanes": ["short_term"],
|
||||
"preferred_lanes": ["short_term", "medium_term"],
|
||||
"allowed_setups": ["breakout_pullback", "breakout_confirmation", "range_reversal"],
|
||||
"preferred_entry_types": ["limit", "market"],
|
||||
}
|
||||
)
|
||||
no_trade_reasons.append("趋势与震荡切换期,方向优势不足")
|
||||
no_trade_reasons.append("趋势与震荡切换期,只做边界确认和突破回踩")
|
||||
|
||||
elif range_regime in {"weak_trend", "strong_trend"} and trend_direction in {"uptrend", "downtrend"}:
|
||||
allow_reversal = bool(reversal_detection.get("is_reversing")) and trend_strength != "strong"
|
||||
@ -127,7 +127,7 @@ class RegimeEngine:
|
||||
"risk_mode": "normal" if range_regime == "strong_trend" else "reduced",
|
||||
"allowed_lanes": ["medium_term", "short_term"],
|
||||
"preferred_lanes": ["medium_term", "short_term"],
|
||||
"allowed_setups": ["trend_continuation_pullback"],
|
||||
"allowed_setups": ["trend_continuation_pullback", "deep_pullback_continuation"],
|
||||
"preferred_entry_types": ["limit", "market"],
|
||||
}
|
||||
)
|
||||
|
||||
@ -42,6 +42,14 @@ class SetupPolicy:
|
||||
continue
|
||||
|
||||
if setup_type not in allowed_setups:
|
||||
if tradability == "selective" and self._allow_selective_fallback(signal, lane=lane):
|
||||
kept.append({
|
||||
**signal,
|
||||
"setup_type": setup_type,
|
||||
"setup_basis": setup_basis,
|
||||
"entry_basis": entry_basis,
|
||||
})
|
||||
continue
|
||||
reasons.append(f"{setup_type} 不在允许 setup 内")
|
||||
reason_counts[f"setup_blocked:{setup_type}"] += 1
|
||||
continue
|
||||
@ -55,6 +63,28 @@ class SetupPolicy:
|
||||
|
||||
return kept, reasons, dict(reason_counts)
|
||||
|
||||
def _allow_selective_fallback(self, signal: Dict[str, Any], *, lane: str) -> bool:
|
||||
confidence = float(signal.get("confidence", 0) or 0)
|
||||
entry_type = signal.get("entry_type", "market")
|
||||
location_tag = ((signal.get("market_location") or {}).get("location_tag") or "")
|
||||
volume_context = signal.get("volume_price_context") or {}
|
||||
pullback_quality = volume_context.get("pullback_quality") or signal.get("pullback_quality")
|
||||
breakout_quality = volume_context.get("breakout_quality") or signal.get("breakout_quality")
|
||||
|
||||
if lane == "medium_term" and entry_type == "limit" and confidence >= 74:
|
||||
if location_tag in {"near_long_zone", "near_short_zone", "near_range_support", "near_range_resistance"}:
|
||||
return True
|
||||
if pullback_quality == "healthy_pullback":
|
||||
return True
|
||||
|
||||
if lane == "short_term" and confidence >= 72:
|
||||
if breakout_quality in {"acceptance_breakout_up", "acceptance_breakout_down"}:
|
||||
return True
|
||||
if entry_type == "limit" and location_tag in {"near_long_zone", "near_short_zone", "near_range_support", "near_range_resistance"}:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _infer_setup_type(self, signal: Dict[str, Any]) -> str:
|
||||
lane = signal.get("timeframe") or signal.get("type") or "medium_term"
|
||||
action = signal.get("action")
|
||||
|
||||
@ -3701,7 +3701,8 @@
|
||||
<div class="runtime-summary-meta">
|
||||
<div class="runtime-summary-row"><span>最近分析说明</span><strong>${monitor.last_analysis_detail || '-'}</strong></div>
|
||||
<div class="runtime-summary-row"><span>最近完成轮次</span><strong>${monitor.last_cycle_completed_at ? `${relativeTime(monitor.last_cycle_completed_at)} / ${formatTime(monitor.last_cycle_completed_at)}` : '暂无'}</strong></div>
|
||||
<div class="runtime-summary-row"><span>日内 / 趋势冷却</span><strong>${schedule.intraday_cooldown_minutes || '-'}m / ${schedule.trend_cooldown_minutes || '-'}m</strong></div>
|
||||
<div class="runtime-summary-row"><span>日内 / 趋势冷却</span><strong>${schedule.intraday_cooldown_minutes ?? '-'}m / ${schedule.trend_cooldown_minutes ?? '-'}m</strong></div>
|
||||
<div class="runtime-summary-row"><span>日内 / 趋势阈值</span><strong>${schedule.intraday_signal_threshold || '-'}% / ${schedule.trend_signal_threshold || '-'}%</strong></div>
|
||||
<div class="runtime-summary-row"><span>事件分析</span><strong>${schedule.event_analysis_enabled ? '开启' : '关闭'}</strong></div>
|
||||
<div class="runtime-summary-row"><span>触发 / LLM</span><strong>${funnel.total_triggers || 0} / ${funnel.llm_analyses || 0}</strong></div>
|
||||
<div class="runtime-summary-row"><span>波动率跳过 / 数据异常</span><strong>${funnel.volatility_skips || 0} / ${funnel.data_invalid_skips || 0}</strong></div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user