Skip to content

Commit d8be210

Browse files
Pigbibicursoragent
andcommitted
Merge main and resolve plugin copy conflicts.
Co-authored-by: Cursor <cursoragent@cursor.com>
2 parents 34caaf8 + 8c8b7e7 commit d8be210

3 files changed

Lines changed: 66 additions & 14 deletions

File tree

notifications/telegram.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@
1717
except ImportError: # pragma: no cover - compatibility with older pinned shared wheels
1818
_merge_strategy_plugin_i18n = None
1919

20+
_TELEGRAM_MARKET_SYMBOL_LINK_RE = re.compile(r"(?<![A-Za-z0-9_])([A-Z0-9]{1,12})\.([A-Z]{2,4})(?![A-Za-z0-9_])")
21+
_TELEGRAM_MARKET_SYMBOL_LINK_JOINER = "\u2060"
22+
23+
24+
def _break_telegram_market_symbol_auto_links(value: object) -> str:
25+
text = str(value or "")
26+
return _TELEGRAM_MARKET_SYMBOL_LINK_RE.sub(
27+
lambda match: f"{match.group(1)}.{_TELEGRAM_MARKET_SYMBOL_LINK_JOINER}{match.group(2)}",
28+
text,
29+
)
30+
31+
2032
_PRICE_SOURCE_LABELS = {
2133
"longbridge_candlesticks": ("LongBridge 日线K线", "LongBridge daily candlesticks"),
2234
"schwab_daily_history_with_live_quote_overlay": ("Schwab 日线历史", "Schwab daily history"),
@@ -185,12 +197,13 @@ def format_small_account_whole_share_bootstrap_notes(
185197
"strategy_plugin_line": "🧩 插件:{plugin} | 启用:{enabled} | 状态:{route} | 提醒:{action}",
186198
"strategy_plugin_enabled_true": "是",
187199
"strategy_plugin_enabled_false": "否",
188-
"strategy_plugin_consumption_auto": "🧩 插件消费:已按策略规则参与本轮仓位计算",
189-
"strategy_plugin_consumption_auto_defend": "🧩 插件消费:已按策略规则参与本轮仓位计算;风险仓位按防守规则处理",
190-
"strategy_plugin_consumption_auto_delever": "🧩 插件消费:已按策略规则参与本轮仓位计算;杠杆仓位按降档规则缩放",
191-
"strategy_plugin_consumption_loaded_not_applied": "🧩 插件消费:已加载但未改写仓位;当前策略未启用该状态的自动消费",
192-
"strategy_plugin_consumption_review_only": "🧩 插件消费:仅通知复核,未参与自动仓位计算",
193-
"strategy_plugin_consumption_unavailable": "🧩 插件消费:未消费插件信号",
200+
"strategy_plugin_consumption_auto": "🧩 插件本次影响:已按策略规则参与本轮仓位计算",
201+
"strategy_plugin_consumption_auto_defend": "🧩 插件本次影响:已触发防守规则并参与本轮仓位计算",
202+
"strategy_plugin_consumption_auto_delever": "🧩 插件本次影响:已触发降杠杆规则并参与本轮仓位计算",
203+
"strategy_plugin_consumption_loaded_not_applied": "🧩 插件本次影响:已加载;该状态未启用自动仓位改写",
204+
"strategy_plugin_consumption_review_only": "🧩 插件本次影响:仅通知复核;当前状态未触发自动仓位改写",
205+
"strategy_plugin_consumption_unavailable": "🧩 插件本次影响:未加载可用插件信号",
206+
194207
"strategy_plugin_alert_subject": "🚨 策略插件告警:{plugin} | {route}",
195208
"strategy_plugin_alert_title": "🚨 【策略插件告警】",
196209
"strategy_plugin_alert_context": "运行环境:{context}",
@@ -346,12 +359,13 @@ def format_small_account_whole_share_bootstrap_notes(
346359
"strategy_plugin_line": "🧩 Plugin: {plugin} | enabled: {enabled} | status: {route} | notice: {action}",
347360
"strategy_plugin_enabled_true": "yes",
348361
"strategy_plugin_enabled_false": "no",
349-
"strategy_plugin_consumption_auto": "🧩 Plugin consumption: included in this cycle's position calculation under strategy rules",
350-
"strategy_plugin_consumption_auto_defend": "🧩 Plugin consumption: included in this cycle's position calculation; risk exposure follows defensive rules",
351-
"strategy_plugin_consumption_auto_delever": "🧩 Plugin consumption: included in this cycle's position calculation; leveraged exposure follows de-risking rules",
352-
"strategy_plugin_consumption_loaded_not_applied": "🧩 Plugin consumption: loaded but did not rewrite positions; this strategy does not enable automatic consumption for this state",
353-
"strategy_plugin_consumption_review_only": "🧩 Plugin consumption: review-only notice, not used for automatic position calculation",
354-
"strategy_plugin_consumption_unavailable": "🧩 Plugin consumption: no plugin signal consumed",
362+
"strategy_plugin_consumption_auto": "🧩 Plugin impact this run: included in this cycle's position calculation under strategy rules",
363+
"strategy_plugin_consumption_auto_defend": "🧩 Plugin impact this run: triggered defensive rules and joined this cycle's position calculation",
364+
"strategy_plugin_consumption_auto_delever": "🧩 Plugin impact this run: triggered de-levering rules and joined this cycle's position calculation",
365+
"strategy_plugin_consumption_loaded_not_applied": "🧩 Plugin impact this run: loaded; this state did not enable automatic position rewrites",
366+
"strategy_plugin_consumption_review_only": "🧩 Plugin impact this run: review-only notice; current state did not trigger automatic position rewrites",
367+
"strategy_plugin_consumption_unavailable": "🧩 Plugin impact this run: no usable plugin signal loaded",
368+
355369
"strategy_plugin_alert_subject": "🚨 Strategy plugin alert: {plugin} | {route}",
356370
"strategy_plugin_alert_title": "🚨 【Strategy Plugin Alert】",
357371
"strategy_plugin_alert_context": "Context: {context}",
@@ -522,7 +536,11 @@ def send_tg_message(message: str) -> None:
522536
return
523537
url = f"https://api.telegram.org/bot{token}/sendMessage"
524538
try:
525-
requests_module.post(url, json={"chat_id": chat_id, "text": message}, timeout=15)
539+
requests_module.post(
540+
url,
541+
json={"chat_id": chat_id, "text": _break_telegram_market_symbol_auto_links(message)},
542+
timeout=15,
543+
)
526544
except Exception as exc:
527545
print(f"Telegram send failed: {type(exc).__name__}", flush=True)
528546

tests/test_notifications.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import sys
2+
from pathlib import Path
3+
4+
5+
ROOT = Path(__file__).resolve().parents[1]
6+
if str(ROOT) not in sys.path:
7+
sys.path.insert(0, str(ROOT))
8+
PLATFORM_KIT_SRC = ROOT.parent / "QuantPlatformKit" / "src"
9+
if str(PLATFORM_KIT_SRC) not in sys.path:
10+
sys.path.insert(0, str(PLATFORM_KIT_SRC))
11+
12+
from notifications.telegram import build_sender
13+
14+
15+
class FakeRequests:
16+
def __init__(self):
17+
self.calls = []
18+
19+
def post(self, url, json, timeout):
20+
self.calls.append((url, json, timeout))
21+
return object()
22+
23+
24+
def test_build_sender_breaks_market_symbol_auto_links():
25+
fake_requests = FakeRequests()
26+
sender = build_sender("token-1", "chat-1", requests_module=fake_requests)
27+
sender("SOXL.US and 00700.HK")
28+
29+
assert len(fake_requests.calls) == 1
30+
url, payload, timeout = fake_requests.calls[0]
31+
assert "token-1" in url
32+
assert payload["chat_id"] == "chat-1"
33+
assert payload["text"] == "SOXL.\u2060US and 00700.\u2060HK"
34+
assert timeout == 15

tests/test_rebalance_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ def test_run_strategy_cycle_strategy_plugin_load_error_is_non_blocking(monkeypat
453453
assert result["strategy_plugin_error"].startswith("JSONDecodeError:")
454454
assert result["strategy_plugin_error_lines"] == (
455455
"⚠️ Plugin signal failed to load: invalid plugin mount JSON; this run falls back to built-in strategy rules",
456-
"🧩 Plugin consumption: no plugin signal consumed",
456+
"🧩 Plugin impact this run: no usable plugin signal loaded",
457457
)
458458
assert result["strategy_plugin_alert_email_sent_count"] == 0
459459
assert result["strategy_plugin_alert_sms_sent_count"] == 0

0 commit comments

Comments
 (0)