Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "quant-platform-kit"
version = "0.7.26"
version = "0.7.27"
description = "Shared broker adapters, domain models, execution ports, and notification utilities for QuantStrategyLab strategies."
readme = "README.md"
requires-python = ">=3.9"
Expand Down
87 changes: 60 additions & 27 deletions src/quant_platform_kit/common/strategy_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,9 +475,18 @@ def build_strategy_plugin_alert_messages(
route = getattr(signal, "canonical_route", None) or "unknown_route"
action = getattr(signal, "suggested_action", None) or "unknown_action"
plugin = translate_strategy_plugin_value("name", getattr(signal, "plugin", None), translator=translator)
translated_mode = translate_strategy_plugin_value(
"mode",
getattr(signal, "effective_mode", None),
translator=translator,
)
translated_route = translate_strategy_plugin_value("route", route, translator=translator)
translated_action = translate_strategy_plugin_value("action", action, translator=translator)
strategy = str(strategy_label or getattr(signal, "strategy", None) or "").strip() or "unknown"
would_trade = _translate_bool(
translator,
bool(getattr(signal, "would_trade_if_enabled", False)),
)
subject = _translate(
translator,
"strategy_plugin_alert_subject",
Expand All @@ -490,6 +499,7 @@ def build_strategy_plugin_alert_messages(
subject = f"[{context}] {subject}"
body_lines = [
_translate(translator, "strategy_plugin_alert_title", fallback="Strategy Plugin Alert"),
"",
]
if context:
body_lines.append(
Expand All @@ -502,33 +512,48 @@ def build_strategy_plugin_alert_messages(
)
body_lines.extend(
[
_translate(
translator,
"strategy_plugin_line",
fallback="Plugin: {plugin} | status: {route} | notice: {action}",
plugin=plugin,
mode=translate_strategy_plugin_value("mode", getattr(signal, "effective_mode", None), translator=translator),
route=translated_route,
action=translated_action,
),
_translate(
translator,
"strategy_plugin_alert_strategy",
fallback="Strategy: {strategy}",
strategy=strategy,
),
_translate(
translator,
"strategy_plugin_alert_as_of",
fallback="Signal as-of: {as_of}",
as_of=getattr(signal, "as_of", None) or "unknown",
),
_translate(
translator,
"strategy_plugin_alert_would_trade",
fallback="Would trade if enabled: {value}",
value=str(bool(getattr(signal, "would_trade_if_enabled", False))).lower(),
),
_translate(
translator,
"strategy_plugin_alert_strategy",
fallback="Strategy: {strategy}",
strategy=strategy,
),
_translate(
translator,
"strategy_plugin_alert_plugin",
fallback="Plugin: {plugin}",
plugin=plugin,
Comment on lines +523 to +525

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep backward-compatible translation fallback for alert fields

In build_strategy_plugin_alert_messages, the alert body now relies on new keys (strategy_plugin_alert_plugin/status/action/mode) and no longer calls the existing strategy_plugin_line key that prior translators already implement. For deployments whose translator dictionaries were not updated in lockstep, these fields now fall back to hardcoded English, producing mixed-language alert bodies and a localization regression introduced by this commit. Consider preserving a compatibility path (e.g., fallback to strategy_plugin_line when the new keys are missing) so patch upgrades do not silently degrade translated alerts.

Useful? React with 👍 / 👎.

),
_translate(
translator,
"strategy_plugin_alert_status",
fallback="Status: {route}",
route=translated_route,
),
_translate(
translator,
"strategy_plugin_alert_action",
fallback="Notice: {action}",
action=translated_action,
),
_translate(
translator,
"strategy_plugin_alert_mode",
fallback="Mode: {mode}",
mode=translated_mode,
),
_translate(
translator,
"strategy_plugin_alert_as_of",
fallback="Signal as-of: {as_of}",
as_of=getattr(signal, "as_of", None) or "unknown",
),
_translate(
translator,
"strategy_plugin_alert_would_trade",
fallback="Would trade if enabled: {value}",
value=would_trade,
),
]
)
source = getattr(signal, "source_uri", None) or getattr(signal, "local_path", None)
Expand Down Expand Up @@ -661,6 +686,14 @@ def _translate(
return translated if translated != key else fallback.format(**kwargs)


def _translate_bool(translator: Callable[..., str] | None, value: bool) -> str:
return _translate(
translator,
"strategy_plugin_alert_yes" if value else "strategy_plugin_alert_no",
fallback="yes" if value else "no",
)


def _required_string(value: Any, *, field_name: str) -> str:
text = _optional_string(value)
if text is None:
Expand Down
11 changes: 10 additions & 1 deletion tests/test_strategy_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,14 @@ def test_strategy_plugin_true_crisis_builds_generic_alert_message(self):
"strategy_plugin_alert_title": "alert title",
"strategy_plugin_line": "plugin={plugin}|mode={mode}|route={route}|action={action}",
"strategy_plugin_alert_strategy": "strategy={strategy}",
"strategy_plugin_alert_plugin": "plugin={plugin}",
"strategy_plugin_alert_status": "status={route}",
"strategy_plugin_alert_action": "action={action}",
"strategy_plugin_alert_mode": "mode={mode}",
"strategy_plugin_alert_as_of": "as_of={as_of}",
"strategy_plugin_alert_would_trade": "would_trade={value}",
"strategy_plugin_alert_source": "source={source}",
"strategy_plugin_alert_yes": "yes",
"strategy_plugin_name_crisis_response_shadow": "Crisis",
"strategy_plugin_mode_shadow": "shadow",
"strategy_plugin_route_true_crisis": "true crisis",
Expand All @@ -294,7 +299,11 @@ def test_strategy_plugin_true_crisis_builds_generic_alert_message(self):
self.assertEqual(len(alerts), 1)
self.assertEqual(alerts[0].subject, "alert:TQQQ Growth Income:Crisis:true crisis")
self.assertIn("strategy_plugin_alert", alerts[0].alert_key)
self.assertIn("plugin=Crisis|mode=shadow|route=true crisis|action=defend", alerts[0].body)
self.assertIn("plugin=Crisis", alerts[0].body)
self.assertIn("status=true crisis", alerts[0].body)
self.assertIn("action=defend", alerts[0].body)
self.assertIn("mode=shadow", alerts[0].body)
self.assertIn("would_trade=yes", alerts[0].body)
self.assertIn("source=gs://bucket/latest_signal.json", alerts[0].body)


Expand Down