feat: Introduce Telegram trade alert functionality and a /tradealert command for previewing alerts.
This commit is contained in:
+78
-1
@@ -10,8 +10,9 @@ if project_root not in sys.path:
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
from src.utils.config_loader import load_config # type: ignore # noqa: E402
|
||||
from src.utils.telegram_push import start_trade_alert_push_loop # type: ignore # noqa: E402
|
||||
from src.utils.telegram_push import start_trade_alert_push_loop, build_trade_alert_for_city # type: ignore # noqa: E402
|
||||
from src.data_collection.weather_sources import WeatherDataCollector # type: ignore # noqa: E402
|
||||
from src.data_collection.city_registry import ALIASES, CITY_REGISTRY # type: ignore # noqa: E402
|
||||
from src.data_collection.city_risk_profiles import get_city_risk_profile # type: ignore # noqa: E402
|
||||
from src.analysis.deb_algorithm import calculate_dynamic_weights, update_daily_record # noqa: E402
|
||||
|
||||
@@ -41,6 +42,7 @@ def start_bot():
|
||||
"可用指令:\n"
|
||||
"/city [城市名] - 查询城市天气预测与实测\n"
|
||||
"/deb [城市名] - 查看 DEB 融合预测准确率\n"
|
||||
"/tradealert [城市名] - 预览当前异动预警推送\n"
|
||||
"/id - 获取当前聊天的 Chat ID\n\n"
|
||||
"示例: <code>/city 伦敦</code>"
|
||||
)
|
||||
@@ -236,6 +238,81 @@ def start_bot():
|
||||
except Exception as e:
|
||||
bot.reply_to(message, f"❌ 查询失败: {e}")
|
||||
|
||||
@bot.message_handler(commands=["tradealert"])
|
||||
def tradealert_preview(message):
|
||||
"""Preview the current trade-alert push payload for one city."""
|
||||
try:
|
||||
parts = message.text.split(maxsplit=1)
|
||||
if len(parts) < 2:
|
||||
bot.reply_to(
|
||||
message,
|
||||
"用法: <code>/tradealert ankara</code>",
|
||||
parse_mode="HTML",
|
||||
)
|
||||
return
|
||||
|
||||
city_input = parts[1].strip().lower()
|
||||
city_name = ALIASES.get(city_input)
|
||||
|
||||
if not city_name and city_input in CITY_REGISTRY:
|
||||
city_name = city_input
|
||||
|
||||
if not city_name and len(city_input) >= 2:
|
||||
for alias, resolved in ALIASES.items():
|
||||
if alias.startswith(city_input):
|
||||
city_name = resolved
|
||||
break
|
||||
if not city_name:
|
||||
for full_name in CITY_REGISTRY:
|
||||
if full_name.startswith(city_input):
|
||||
city_name = full_name
|
||||
break
|
||||
|
||||
if not city_name:
|
||||
bot.reply_to(
|
||||
message,
|
||||
f"❌ 未找到城市: <b>{city_input}</b>",
|
||||
parse_mode="HTML",
|
||||
)
|
||||
return
|
||||
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
f"📡 正在生成 {city_name.title()} 的异动预警预览...",
|
||||
)
|
||||
|
||||
payload = build_trade_alert_for_city(
|
||||
city_name,
|
||||
config,
|
||||
force_refresh=True,
|
||||
)
|
||||
|
||||
preview = ((payload.get("telegram") or {}).get("zh") or "").strip()
|
||||
trigger_count = int(payload.get("trigger_count") or 0)
|
||||
severity = str(payload.get("severity") or "none")
|
||||
header = (
|
||||
f"[TEST PREVIEW] city={city_name} "
|
||||
f"severity={severity} triggers={trigger_count}"
|
||||
)
|
||||
|
||||
if not preview:
|
||||
bot.reply_to(message, f"{header}\n\nNo Telegram preview available.")
|
||||
return
|
||||
|
||||
if trigger_count <= 0:
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
f"{header}\n\n当前没有 active trade alert。\n\n{preview}",
|
||||
)
|
||||
return
|
||||
|
||||
bot.send_message(message.chat.id, f"{header}\n\n{preview}")
|
||||
except Exception as e:
|
||||
import traceback
|
||||
|
||||
logger.error(f"trade alert preview failed: {e}\n{traceback.format_exc()}")
|
||||
bot.reply_to(message, f"❌ tradealert failed: {e}")
|
||||
|
||||
@bot.message_handler(commands=["city"])
|
||||
def get_city_info(message):
|
||||
"""查询指定城市的天气详情"""
|
||||
|
||||
@@ -134,12 +134,16 @@ def _alert_signature(alert_payload: Dict[str, Any]) -> str:
|
||||
return hashlib.sha1(raw.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def _build_trade_alert_for_city(city: str, config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
def build_trade_alert_for_city(
|
||||
city: str,
|
||||
config: Dict[str, Any],
|
||||
force_refresh: bool = False,
|
||||
) -> Dict[str, Any]:
|
||||
from web.app import _analyze
|
||||
from src.analysis.market_alert_engine import build_trading_alerts
|
||||
from src.data_collection.polymarket_client import build_city_market_snapshot
|
||||
|
||||
city_weather = _analyze(city, force_refresh=False)
|
||||
city_weather = _analyze(city, force_refresh=force_refresh)
|
||||
target_date = city_weather.get("local_date")
|
||||
|
||||
proxy = (
|
||||
@@ -150,7 +154,7 @@ def _build_trade_alert_for_city(city: str, config: Dict[str, Any]) -> Dict[str,
|
||||
city=city,
|
||||
target_date=target_date,
|
||||
proxy=proxy,
|
||||
force_refresh=False,
|
||||
force_refresh=force_refresh,
|
||||
)
|
||||
map_url = os.getenv("POLYWEATHER_MAP_URL") or "https://polyweather.vercel.app"
|
||||
alert_payload = build_trading_alerts(
|
||||
@@ -231,7 +235,7 @@ def start_trade_alert_push_loop(bot: Any, config: Dict[str, Any]) -> Optional[th
|
||||
changed = False
|
||||
for city in cities:
|
||||
try:
|
||||
alert_payload = _build_trade_alert_for_city(city, config)
|
||||
alert_payload = build_trade_alert_for_city(city, config)
|
||||
if _maybe_send_alert(
|
||||
bot=bot,
|
||||
chat_id=chat_id,
|
||||
@@ -264,4 +268,3 @@ def start_trade_alert_push_loop(bot: Any, config: Dict[str, Any]) -> Optional[th
|
||||
)
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
|
||||
Reference in New Issue
Block a user