Skip to content

Commit 420c332

Browse files
Pigbibicursoragent
andauthored
Gate deploy on CI success and relabel dashboard buying power for cash-only mode. (#209)
Deploy waits for a green CI run on main; notification renderers swap buying power labels based on cash_only_execution. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 312d984 commit 420c332

4 files changed

Lines changed: 86 additions & 9 deletions

File tree

.github/workflows/sync-cloud-run-env.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
name: Deploy Cloud Run
22

33
on:
4-
push:
5-
branches: [ main ]
4+
workflow_run:
5+
workflows: [CI]
6+
types: [completed]
7+
branches: [main]
68
workflow_dispatch:
79
inputs:
810
target:
@@ -63,6 +65,13 @@ concurrency:
6365
jobs:
6466
sync:
6567
name: Deploy / Sync ${{ matrix.target.label }} Cloud Run
68+
if: >
69+
github.event_name == 'workflow_dispatch' ||
70+
(
71+
github.event_name == 'workflow_run' &&
72+
github.event.workflow_run.conclusion == 'success' &&
73+
github.event.workflow_run.event == 'push'
74+
)
6675
runs-on: ubuntu-latest
6776
timeout-minutes: 20
6877
strategy:
@@ -79,6 +88,7 @@ jobs:
7988
environment: longbridge-sg
8089
default_account_region: SG
8190
permissions:
91+
actions: read
8292
contents: read
8393
id-token: write
8494
environment: ${{ matrix.target.environment }}
@@ -257,6 +267,8 @@ jobs:
257267
- name: Checkout repository
258268
if: steps.config.outputs.enabled == 'true'
259269
uses: actions/checkout@v6
270+
with:
271+
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }}
260272

261273
- name: Apply HK verify-only dispatch defaults
262274
if: steps.config.outputs.enabled == 'true' && github.event_name == 'workflow_dispatch' && inputs.target == 'hk-verify'

notifications/renderers.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,22 @@ def _build_risk_control_lines(execution, *, translator):
331331
return []
332332

333333

334-
def _format_dashboard_text(text, *, translator=None) -> str:
334+
def _relabel_dashboard_buying_power(text: str, *, cash_only_execution: bool, translator) -> str:
335+
value = str(text or "")
336+
if cash_only_execution:
337+
target = translator("buying_power")
338+
for source in ("Buying power", "购买力"):
339+
if source != target:
340+
value = value.replace(source, target)
341+
return value
342+
target = translator("buying_power_margin")
343+
for source in ("Available cash", "可用现金"):
344+
if source != target:
345+
value = value.replace(source, target)
346+
return value
347+
348+
349+
def _format_dashboard_text(text, *, translator=None, cash_only_execution: bool = True) -> str:
335350
lines = []
336351
for raw_line in str(text or "").splitlines():
337352
line = raw_line.rstrip()
@@ -340,11 +355,23 @@ def _format_dashboard_text(text, *, translator=None) -> str:
340355
if translator is not None and _translator_uses_zh(translator):
341356
line = _localize_notification_text(line, translator=translator)
342357
lines.append(line)
343-
return "\n".join(lines)
358+
result = "\n".join(lines)
359+
if translator is not None:
360+
result = _relabel_dashboard_buying_power(
361+
result,
362+
cash_only_execution=cash_only_execution,
363+
translator=translator,
364+
)
365+
return result
344366

345367

346368
def _append_dashboard_block(lines, *, execution, separator, translator) -> None:
347-
dashboard_text = _format_dashboard_text(execution.get("dashboard_text"), translator=translator)
369+
cash_only_execution = bool(execution.get("cash_only_execution", True))
370+
dashboard_text = _format_dashboard_text(
371+
execution.get("dashboard_text"),
372+
translator=translator,
373+
cash_only_execution=cash_only_execution,
374+
)
348375
if dashboard_text:
349376
lines.append(separator)
350377
lines.extend(dashboard_text.splitlines())

tests/test_notifications.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
build_strategy_display_name,
1515
build_translator,
1616
)
17-
from notifications.renderers import render_heartbeat_notification
17+
from notifications.renderers import render_heartbeat_notification, render_rebalance_notification
1818
from strategy_registry import SUPPORTED_STRATEGY_PROFILES
1919

2020

@@ -388,6 +388,44 @@ def test_heartbeat_localizes_strategy_diagnostics_and_source_input_status(self):
388388
self.assertNotIn("target_stock=", rendered.compact_text)
389389
self.assertNotIn("partial_history_refresh", rendered.compact_text)
390390

391+
def test_dashboard_relabels_buying_power_for_cash_only_execution(self):
392+
rendered = render_rebalance_notification(
393+
execution={
394+
"dashboard_text": " - 购买力: $100.00 | 可投资现金: $50.00",
395+
"cash_only_execution": True,
396+
"signal_display": "hold",
397+
"status_display": "hold",
398+
},
399+
logs=(),
400+
skip_logs=(),
401+
note_logs=(),
402+
translator=build_translator("zh"),
403+
separator="━━━━━━━━━━━━━━━━━━",
404+
strategy_display_name="demo",
405+
dry_run_only=False,
406+
)
407+
self.assertIn("可用现金: $100.00", rendered.compact_text)
408+
self.assertNotIn("购买力: $100.00", rendered.compact_text)
409+
410+
def test_dashboard_keeps_margin_buying_power_label_when_cash_only_disabled(self):
411+
rendered = render_rebalance_notification(
412+
execution={
413+
"dashboard_text": " - Available cash: $100.00 | Investable cash: $50.00",
414+
"cash_only_execution": False,
415+
"signal_display": "hold",
416+
"status_display": "hold",
417+
},
418+
logs=(),
419+
skip_logs=(),
420+
note_logs=(),
421+
translator=build_translator("en"),
422+
separator="━━━━━━━━━━━━━━━━━━",
423+
strategy_display_name="demo",
424+
dry_run_only=False,
425+
)
426+
self.assertIn("Buying power: $100.00", rendered.compact_text)
427+
self.assertNotIn("Available cash: $100.00", rendered.compact_text)
428+
391429
def test_build_prefixer_prefers_account_prefix_only(self):
392430
with_prefix = build_prefixer("HK", "longbridge-quant-semiconductor-rotation-income-hk")
393431
self.assertEqual(with_prefix("hello"), "[HK] hello")

tests/test_rebalance_service.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def _build_plan(
7171
dashboard_lines = [
7272
"📌 策略账户概览",
7373
f" - 总资产(策略标的+现金): ${float(total_strategy_equity):,.2f}",
74-
f" - 购买力: ${float(available_cash):.2f} | 可投资现金: ${float(investable_cash):.2f}",
74+
f" - 可用现金: ${float(available_cash):.2f} | 可投资现金: ${float(investable_cash):.2f}",
7575
]
7676
nonzero_cash = {
7777
currency: amount
@@ -1899,7 +1899,7 @@ def test_zero_investable_cash_reports_buying_power_without_trade_note(self):
18991899

19001900
self.assertEqual(len(sent_messages), 1)
19011901
self.assertNotIn("账户现金", sent_messages[0])
1902-
self.assertIn("购买力: $3065.61 | 可投资现金: $0.00", sent_messages[0])
1902+
self.assertIn("可用现金: $3065.61 | 可投资现金: $0.00", sent_messages[0])
19031903
self.assertIn("BOXX: $24,880.00 / 214股", sent_messages[0])
19041904
self.assertIn("✅ 无需调仓", sent_messages[0])
19051905
self.assertNotIn("本轮没有可执行订单", sent_messages[0])
@@ -2585,7 +2585,7 @@ def test_heartbeat_accepts_normalized_portfolio_and_execution_sections(self):
25852585
self.assertNotIn("💵 资金\n - 账户现金:", sent_messages[0])
25862586
self.assertIn("📌 策略账户概览", sent_messages[0])
25872587
self.assertIn("总资产(策略标的+现金): $60,000.00", sent_messages[0])
2588-
self.assertIn("购买力: $101.95 | 可投资现金: $101.95", sent_messages[0])
2588+
self.assertIn("可用现金: $101.95 | 可投资现金: $101.95", sent_messages[0])
25892589
self.assertIn("SOXX: $0.00 / 0股", sent_messages[0])
25902590

25912591
def test_hybrid_heartbeat_hides_empty_semiconductor_fields_and_shows_benchmark_line(self):

0 commit comments

Comments
 (0)