Skip to content

Commit 345f7f0

Browse files
Pigbibicodex
andauthored
fix: suppress no-trade cycle notifications (#286)
Co-authored-by: Codex <noreply@openai.com>
1 parent 5831336 commit 345f7f0

8 files changed

Lines changed: 29 additions & 16 deletions

application/rebalance_service.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ def fetch_replanned_state():
396396
title_key=config.notification_title_key or "rebalance_title",
397397
)
398398
)
399-
else:
399+
elif getattr(config, "notify_no_trade_cycles", True):
400400
notification_publisher.publish(
401401
notification_renderers.render_heartbeat_notification(
402402
execution=execution,
@@ -410,4 +410,6 @@ def fetch_replanned_state():
410410
title_key=config.notification_title_key or "heartbeat_title",
411411
)
412412
)
413+
else:
414+
print(config.with_prefix("notification_suppressed reason=no_trade_or_error"), flush=True)
413415
return execution_result

application/runtime_composer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ def build_rebalance_config(
251251
sleeper=self.sleeper,
252252
extra_notification_lines=(market_scope_line, *plugin_lines, *plugin_error_lines),
253253
notification_title_key=notification_title_key,
254+
notify_no_trade_cycles=False,
254255
strategy_plugin_signals=tuple(strategy_plugin_signals or ()),
255256
execution_dedup_enabled=resolve_execution_dedup_enabled(
256257
env_reader=self.env_reader,

application/runtime_dependencies.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class LongBridgeRebalanceConfig:
3232
sleeper: Callable[[float], None] | None = None
3333
extra_notification_lines: tuple[str, ...] = ()
3434
notification_title_key: str = ""
35+
notify_no_trade_cycles: bool = True
3536
strategy_plugin_signals: tuple[Any, ...] = ()
3637
execution_dedup_enabled: bool = False
3738
execution_state_store: Any = None

constraints.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Generated: 2026-07-01
44
# Auto-updated by update-qpk-pin.yml on every push to QPK main.
55

6-
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@b0eacd2fe4884f7f2447b704a232e9a121f396c4
6+
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@b9a7df85cfc848cebcc3aa6e1d77ec34ca7611ab
77
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@6568c315ce3be6f7ae5b799374cf7fb44232c170
88
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@e9e3058c1eaf3f43b25d50df5eb14442816e568e
99
cn-equity-strategies @ git+https://github.com/QuantStrategyLab/CnEquityStrategies.git@f6c735c33047d7613a23d5df018ed32f394e6001

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ google-cloud-secret-manager
88
google-cloud-storage
99
google-auth
1010
longport==3.0.23
11-
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@b0eacd2fe4884f7f2447b704a232e9a121f396c4
11+
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@b9a7df85cfc848cebcc3aa6e1d77ec34ca7611ab
1212
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@6568c315ce3be6f7ae5b799374cf7fb44232c170
1313
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@e9e3058c1eaf3f43b25d50df5eb14442816e568e

scripts/check_qpk_pin_consistency.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
"""Check that all QPK git references match the canonical QPK_PIN.
33
Usage: python scripts/check_qpk_pin_consistency.py [--fix]
44
"""
5-
import re, subprocess, sys
5+
import re, sys
66
from pathlib import Path
77

88
QPK_PIN_URL = "https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/QPK_PIN"
9-
SHA_RE = re.compile(r"@([a-f0-9]{40})")
9+
QPK_REF_RE = re.compile(r"QuantPlatformKit\.git@([a-f0-9]{40})")
1010

1111
def fetch_pin() -> str:
1212
import urllib.request
@@ -23,15 +23,17 @@ def main():
2323
for path in sorted(Path.cwd().glob("**/requirements*.txt")) + sorted(Path.cwd().glob("**/pyproject.toml")):
2424
if "external" in str(path): continue
2525
content = path.read_text()
26-
for m in SHA_RE.finditer(content):
26+
updated = content
27+
for m in QPK_REF_RE.finditer(content):
2728
sha = m.group(1)
28-
if "QuantPlatformKit" not in content[max(0,m.start()-200):m.end()]: continue
2929
if sha != target:
3030
errors += 1
3131
print(f" ❌ {path}: QPK@{sha[:12]} (expected {target[:12]})")
3232
if fix:
33-
path.write_text(content.replace(sha, target))
33+
updated = updated.replace(f"QuantPlatformKit.git@{sha}", f"QuantPlatformKit.git@{target}")
3434
print(" → fixed")
35+
if fix and updated != content:
36+
path.write_text(updated)
3537
if errors:
3638
print(f"\n{errors} mismatch(es). Run with --fix to auto-fix.")
3739
return 1

tests/test_rebalance_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -870,14 +870,14 @@ def test_run_strategy_prefers_portfolio_port_runtime_path(self):
870870
translator=build_translator("zh"),
871871
with_prefix=lambda message: f"[HK/LongBridgeQuant] {message}",
872872
strategy_display_name="SOXL/SOXX 半导体趋势收益",
873+
notify_no_trade_cycles=False,
873874
),
874875
)
875876

876877
self.assertIs(observed["snapshot"], snapshot)
877878
self.assertIsNone(observed["account_state"])
878879
self.assertEqual(observed["indicators"], {"soxl": {"price": 1.0}})
879-
self.assertEqual(len(sent_messages), 1)
880-
self.assertIn("【心跳", sent_messages[0])
880+
self.assertEqual(sent_messages, [])
881881

882882
def test_run_strategy_supports_execution_port_runtime_path(self):
883883
sent_messages = []

tests/test_runtime_composer.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
sys.path.insert(0, str(ROOT))
99

1010
from quant_platform_kit.common import build_runtime_target # noqa: E402
11+
from application import runtime_composer as runtime_composer_module
1112
from application.runtime_composer import LongBridgeRuntimeComposer
1213

1314

14-
def test_runtime_composer_builds_runtime_and_config_from_local_builders():
15+
def test_runtime_composer_builds_runtime_and_config_from_local_builders(monkeypatch):
1516
observed = {}
1617

1718
def fake_notification_builder(**kwargs):
@@ -30,6 +31,15 @@ def fake_bootstrap_builder(**kwargs):
3031
observed["bootstrap_builder"] = kwargs
3132
return "bootstrap"
3233

34+
def fake_cycle_sender(**kwargs):
35+
observed["cycle_sender"] = kwargs
36+
return lambda message: observed.setdefault(
37+
"sent_message",
38+
(kwargs["telegram_token"], kwargs["telegram_chat_id"], message),
39+
)
40+
41+
monkeypatch.setattr(runtime_composer_module, "build_cycle_sender", fake_cycle_sender)
42+
3343
composer = LongBridgeRuntimeComposer(
3444
project_id="project-1",
3545
secret_name="secret-1",
@@ -84,10 +94,6 @@ def fake_bootstrap_builder(**kwargs):
8494
report_persister="report-persister",
8595
translator=lambda key, **_kwargs: key,
8696
prefixer_builder=lambda prefix: lambda message: f"[{prefix}] {message}",
87-
sender_builder=lambda token, chat_id, *, with_prefix_fn: lambda message: observed.setdefault(
88-
"sent_message",
89-
(token, chat_id, with_prefix_fn(message)),
90-
),
9197
env_reader=lambda name, default="": {
9298
"K_SERVICE": "longbridge-platform",
9399
"EXECUTION_REPORT_OUTPUT_DIR": "/tmp/runtime-reports",
@@ -140,6 +146,7 @@ def fake_bootstrap_builder(**kwargs):
140146
assert config.dry_run_only is True
141147
assert config.safe_haven_cash_substitute_threshold_usd == 1000.0
142148
assert config.min_order_notional_usd == 100.0
149+
assert config.notify_no_trade_cycles is False
143150
assert config.execution_dedup_enabled is True
144151
assert config.execution_state_account_scope == "HK"
145-
assert config.execution_state_store.gcs_prefix_uri == "gs://bucket/runtime-reports"
152+
assert config.execution_state_store.cloud_prefix_uri == "gs://bucket/runtime-reports"

0 commit comments

Comments
 (0)