Fix mispricing alerts for bearish low-Yes signals
This commit is contained in:
@@ -811,6 +811,57 @@ def _build_advice_cn(
|
|||||||
return ",".join(parts) + "。"
|
return ",".join(parts) + "。"
|
||||||
|
|
||||||
|
|
||||||
|
def _classify_low_yes_signal(
|
||||||
|
rules: Dict[str, Dict[str, Any]],
|
||||||
|
market_snapshot: Optional[Dict[str, Any]] = None,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
snapshot = market_snapshot or {}
|
||||||
|
forecast_bucket = snapshot.get("forecast_bucket") or {}
|
||||||
|
yes_buy = _norm_probability(forecast_bucket.get("yes_buy"))
|
||||||
|
center_deb = rules.get("ankara_center_deb_hit", {}) or {}
|
||||||
|
momentum = rules.get("momentum_spike", {}) or {}
|
||||||
|
advection = rules.get("advection", {}) or {}
|
||||||
|
breakthrough = rules.get("forecast_breakthrough", {}) or {}
|
||||||
|
|
||||||
|
cheap_yes = yes_buy is not None and yes_buy < 0.10
|
||||||
|
center_hit = bool(center_deb.get("triggered"))
|
||||||
|
momentum_direction = str(momentum.get("direction") or "neutral").lower()
|
||||||
|
momentum_up = bool(momentum.get("triggered")) and momentum_direction == "up"
|
||||||
|
momentum_down = bool(momentum.get("triggered")) and momentum_direction == "down"
|
||||||
|
warm_flow = bool(advection.get("triggered"))
|
||||||
|
model_break = bool(breakthrough.get("triggered"))
|
||||||
|
|
||||||
|
bullish_confirmation = momentum_up or warm_flow or model_break
|
||||||
|
bearish_conflict = momentum_down and not warm_flow and not model_break
|
||||||
|
|
||||||
|
if cheap_yes and center_hit and bullish_confirmation:
|
||||||
|
return {
|
||||||
|
"label": "undervalued",
|
||||||
|
"should_push": True,
|
||||||
|
"reason_cn": "该桶 Yes 价格 < 10c,且天气信号与上行方向一致,疑似低估",
|
||||||
|
}
|
||||||
|
|
||||||
|
if cheap_yes and center_hit and bearish_conflict:
|
||||||
|
return {
|
||||||
|
"label": "conflicted",
|
||||||
|
"should_push": False,
|
||||||
|
"reason_cn": "该桶 Yes 价格虽 < 10c,但短时动量转弱,暂不判定低估",
|
||||||
|
}
|
||||||
|
|
||||||
|
if cheap_yes:
|
||||||
|
return {
|
||||||
|
"label": "watch",
|
||||||
|
"should_push": False,
|
||||||
|
"reason_cn": "该桶 Yes 价格 < 10c,但方向确认不足,先观察",
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"label": "none",
|
||||||
|
"should_push": False,
|
||||||
|
"reason_cn": "当前未出现明确的低价错配信号",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _build_telegram_messages(
|
def _build_telegram_messages(
|
||||||
city_weather: Dict[str, Any],
|
city_weather: Dict[str, Any],
|
||||||
rules: Dict[str, Dict[str, Any]],
|
rules: Dict[str, Dict[str, Any]],
|
||||||
@@ -1022,6 +1073,7 @@ def _build_telegram_messages_mispricing(
|
|||||||
snapshot = market_snapshot or _extract_market_snapshot(city_weather)
|
snapshot = market_snapshot or _extract_market_snapshot(city_weather)
|
||||||
momentum = rules.get("momentum_spike", {})
|
momentum = rules.get("momentum_spike", {})
|
||||||
center_deb = rules.get("ankara_center_deb_hit", {})
|
center_deb = rules.get("ankara_center_deb_hit", {})
|
||||||
|
signal_state = _classify_low_yes_signal(rules, snapshot)
|
||||||
local_time = str(city_weather.get("local_time") or "").strip()
|
local_time = str(city_weather.get("local_time") or "").strip()
|
||||||
obs_time = str(current.get("obs_time") or "").strip()
|
obs_time = str(current.get("obs_time") or "").strip()
|
||||||
suppressed = bool((suppression or {}).get("suppressed"))
|
suppressed = bool((suppression or {}).get("suppressed"))
|
||||||
@@ -1081,8 +1133,10 @@ def _build_telegram_messages_mispricing(
|
|||||||
if lead_gap is not None:
|
if lead_gap is not None:
|
||||||
center_line += f" | 领先 {lead_gap:+.1f}{temp_symbol}"
|
center_line += f" | 领先 {lead_gap:+.1f}{temp_symbol}"
|
||||||
|
|
||||||
if snapshot.get("available"):
|
if snapshot.get("available") and signal_state.get("should_push"):
|
||||||
title_zh = "🚨 PolyWeather 错价雷达"
|
title_zh = "🚨 PolyWeather 错价雷达"
|
||||||
|
elif snapshot.get("available"):
|
||||||
|
title_zh = "📍 PolyWeather 错价观察"
|
||||||
else:
|
else:
|
||||||
title_zh = "🚨 PolyWeather 异动预警" if (has_active_trigger or suppressed) else "📍 PolyWeather 状态快照"
|
title_zh = "🚨 PolyWeather 异动预警" if (has_active_trigger or suppressed) else "📍 PolyWeather 状态快照"
|
||||||
|
|
||||||
@@ -1101,7 +1155,7 @@ def _build_telegram_messages_mispricing(
|
|||||||
else:
|
else:
|
||||||
lines_zh.append("基准:多模型最高温 --(结算参考 --)")
|
lines_zh.append("基准:多模型最高温 --(结算参考 --)")
|
||||||
lines_zh.append(f"命中桶:{match_bucket_label} | Yes: {match_bucket_yes}")
|
lines_zh.append(f"命中桶:{match_bucket_label} | Yes: {match_bucket_yes}")
|
||||||
lines_zh.append("触发:该桶 Yes 价格 < 10c,疑似低估")
|
lines_zh.append(f"触发:{signal_state.get('reason_cn')}")
|
||||||
if center_line:
|
if center_line:
|
||||||
lines_zh.append(center_line)
|
lines_zh.append(center_line)
|
||||||
lines_zh.append("")
|
lines_zh.append("")
|
||||||
@@ -1119,8 +1173,15 @@ def _build_telegram_messages_mispricing(
|
|||||||
lines_zh.append(f"市场链接:{market_url}")
|
lines_zh.append(f"市场链接:{market_url}")
|
||||||
lines_zh.append(f"地图:{final_map}")
|
lines_zh.append(f"地图:{final_map}")
|
||||||
|
|
||||||
|
title_en = (
|
||||||
|
"🚨 PolyWeather Mispricing Radar"
|
||||||
|
if snapshot.get("available") and signal_state.get("should_push")
|
||||||
|
else "📍 PolyWeather Mispricing Watch"
|
||||||
|
if snapshot.get("available")
|
||||||
|
else "🚨 PolyWeather Alert"
|
||||||
|
)
|
||||||
lines_en = [
|
lines_en = [
|
||||||
f"🚨 PolyWeather Mispricing Radar [{city_name}]",
|
f"{title_en} [{city_name}]",
|
||||||
"",
|
"",
|
||||||
f"Type: {types_cn}",
|
f"Type: {types_cn}",
|
||||||
f"Now: {dynamic_text}",
|
f"Now: {dynamic_text}",
|
||||||
@@ -1172,6 +1233,7 @@ def _build_alert_evidence(
|
|||||||
|
|
||||||
trigger_types = [row.get("type") for row in triggered if row.get("type")]
|
trigger_types = [row.get("type") for row in triggered if row.get("type")]
|
||||||
forecast_bucket = market_snapshot.get("forecast_bucket") or {}
|
forecast_bucket = market_snapshot.get("forecast_bucket") or {}
|
||||||
|
low_yes_signal = _classify_low_yes_signal(rules, market_snapshot)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
@@ -1252,6 +1314,7 @@ def _build_alert_evidence(
|
|||||||
},
|
},
|
||||||
"market": {
|
"market": {
|
||||||
"available": bool(market_snapshot.get("available")),
|
"available": bool(market_snapshot.get("available")),
|
||||||
|
"low_yes_signal": low_yes_signal,
|
||||||
"market_prob": market_snapshot.get("market_prob"),
|
"market_prob": market_snapshot.get("market_prob"),
|
||||||
"model_prob": market_snapshot.get("model_prob"),
|
"model_prob": market_snapshot.get("model_prob"),
|
||||||
"edge_percent": market_snapshot.get("edge_percent"),
|
"edge_percent": market_snapshot.get("edge_percent"),
|
||||||
@@ -1294,6 +1357,7 @@ def build_trading_alerts(
|
|||||||
city = city_weather.get("name", "")
|
city = city_weather.get("name", "")
|
||||||
now = datetime.now(timezone.utc).isoformat()
|
now = datetime.now(timezone.utc).isoformat()
|
||||||
market_snapshot = _extract_market_snapshot(city_weather)
|
market_snapshot = _extract_market_snapshot(city_weather)
|
||||||
|
low_yes_signal = _classify_low_yes_signal({}, {})
|
||||||
|
|
||||||
rules: Dict[str, Dict[str, Any]] = {
|
rules: Dict[str, Dict[str, Any]] = {
|
||||||
"ankara_center_deb_hit": _calc_ankara_center_deb_alert(city_weather, temp_symbol),
|
"ankara_center_deb_hit": _calc_ankara_center_deb_alert(city_weather, temp_symbol),
|
||||||
@@ -1301,6 +1365,7 @@ def build_trading_alerts(
|
|||||||
"forecast_breakthrough": _calc_forecast_breakthrough_alert(city_weather, temp_symbol),
|
"forecast_breakthrough": _calc_forecast_breakthrough_alert(city_weather, temp_symbol),
|
||||||
"advection": _calc_advection_alert(city_weather, temp_symbol),
|
"advection": _calc_advection_alert(city_weather, temp_symbol),
|
||||||
}
|
}
|
||||||
|
low_yes_signal = _classify_low_yes_signal(rules, market_snapshot)
|
||||||
|
|
||||||
triggered = [
|
triggered = [
|
||||||
{
|
{
|
||||||
@@ -1330,13 +1395,22 @@ def build_trading_alerts(
|
|||||||
if force_push and severity == "none":
|
if force_push and severity == "none":
|
||||||
severity = "medium"
|
severity = "medium"
|
||||||
|
|
||||||
telegram = _build_telegram_messages_mispricing(
|
if market_snapshot.get("available") and low_yes_signal.get("should_push"):
|
||||||
city_weather=city_weather,
|
telegram = _build_telegram_messages_mispricing(
|
||||||
rules=rules,
|
city_weather=city_weather,
|
||||||
market_snapshot=market_snapshot,
|
rules=rules,
|
||||||
suppression=suppression,
|
market_snapshot=market_snapshot,
|
||||||
map_url=map_url,
|
suppression=suppression,
|
||||||
)
|
map_url=map_url,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
telegram = _build_telegram_messages(
|
||||||
|
city_weather=city_weather,
|
||||||
|
rules=rules,
|
||||||
|
map_url=map_url,
|
||||||
|
market_snapshot=market_snapshot,
|
||||||
|
suppression=suppression,
|
||||||
|
)
|
||||||
evidence = _build_alert_evidence(
|
evidence = _build_alert_evidence(
|
||||||
city_weather=city_weather,
|
city_weather=city_weather,
|
||||||
rules=rules,
|
rules=rules,
|
||||||
@@ -1359,4 +1433,3 @@ def build_trading_alerts(
|
|||||||
"evidence": evidence,
|
"evidence": evidence,
|
||||||
"telegram": telegram,
|
"telegram": telegram,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user