Skip to content

Commit 5a541fc

Browse files
authored
Show plugin load errors in strategy notifications
1 parent 73c87fc commit 5a541fc

7 files changed

Lines changed: 39 additions & 9 deletions

application/runtime_composer.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,12 @@ def build_rebalance_runtime(
191191
post_submit_order=notification_adapters.post_submit_order,
192192
)
193193

194-
def build_rebalance_config(self, *, strategy_plugin_signals=()) -> LongBridgeRebalanceConfig:
194+
def build_rebalance_config(
195+
self,
196+
*,
197+
strategy_plugin_signals=(),
198+
strategy_plugin_error: str | None = None,
199+
) -> LongBridgeRebalanceConfig:
195200
market_scope_line = self.translator(
196201
"market_scope_detail",
197202
market=self.market,
@@ -204,6 +209,12 @@ def build_rebalance_config(self, *, strategy_plugin_signals=()) -> LongBridgeReb
204209
lambda _signals: (),
205210
)
206211
plugin_lines = tuple(build_plugin_lines(tuple(strategy_plugin_signals or ())))
212+
build_plugin_error_lines = getattr(
213+
self.strategy_adapters,
214+
"build_strategy_plugin_error_notification_lines",
215+
lambda _error: (),
216+
)
217+
plugin_error_lines = tuple(build_plugin_error_lines(strategy_plugin_error))
207218
return LongBridgeRebalanceConfig(
208219
limit_sell_discount=self.limit_sell_discount,
209220
limit_buy_premium=self.limit_buy_premium,
@@ -219,7 +230,7 @@ def build_rebalance_config(self, *, strategy_plugin_signals=()) -> LongBridgeReb
219230
min_order_notional_usd=self.min_order_notional_usd,
220231
safe_haven_cash_substitute_threshold_usd=self.safe_haven_cash_substitute_threshold_usd,
221232
sleeper=self.sleeper,
222-
extra_notification_lines=(market_scope_line, *plugin_lines),
233+
extra_notification_lines=(market_scope_line, *plugin_lines, *plugin_error_lines),
223234
strategy_plugin_signals=tuple(strategy_plugin_signals or ()),
224235
execution_dedup_enabled=resolve_execution_dedup_enabled(
225236
env_reader=self.env_reader,

application/runtime_strategy_adapters.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from quant_platform_kit.common.strategy_plugins import (
1010
build_strategy_plugin_alert_messages,
11+
build_strategy_plugin_error_notification_lines,
1112
build_strategy_plugin_notification_lines,
1213
should_alert_strategy_plugin_signal,
1314
translate_strategy_plugin_value,
@@ -89,6 +90,9 @@ def translate_strategy_plugin_value(self, category: str, raw_value: str | None)
8990
def build_strategy_plugin_notification_lines(self, signals) -> tuple[str, ...]:
9091
return build_strategy_plugin_notification_lines(signals, translator=self.translator)
9192

93+
def build_strategy_plugin_error_notification_lines(self, error) -> tuple[str, ...]:
94+
return build_strategy_plugin_error_notification_lines(error, translator=self.translator)
95+
9296
def should_alert_strategy_plugin_signal(self, signal) -> bool:
9397
return should_alert_strategy_plugin_signal(signal)
9498

main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,10 @@ def run_strategy(*, force_run: bool = False, validation_only: bool = False, vali
502502
)
503503
cycle_result = run_rebalance_cycle(
504504
runtime=rebalance_runtime,
505-
config=composer.build_rebalance_config(strategy_plugin_signals=strategy_plugin_signals),
505+
config=composer.build_rebalance_config(
506+
strategy_plugin_signals=strategy_plugin_signals,
507+
strategy_plugin_error=strategy_plugin_error,
508+
),
506509
)
507510
signal_snapshot = {}
508511
if cycle_result is not None:

requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
flask
22
gunicorn
3-
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@2a711adf60b585ca02932bab9ee1bac7ce1df7c6
4-
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@fdd39ef0313181bee9083319b87b7175c32b364d
5-
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@02af62bc7af7b8ffdbe8575421434e455ab00d66
3+
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@1a31ddde0dde0f2ad423e841a84af3ba0869e612
4+
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@0ad01faaa2741195d06ecf8dae6a0f3fda712080
5+
hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@1c50d2fcedc41b36d387366d0a9209ee759f0308
66
pandas
77
requests
88
pytz

tests/test_request_handling.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ def with_prefix(self, message):
489489
def build_rebalance_runtime(self, *, silent_cycle_notifications=False):
490490
return types.SimpleNamespace()
491491

492-
def build_rebalance_config(self, *, strategy_plugin_signals=()):
492+
def build_rebalance_config(self, *, strategy_plugin_signals=(), strategy_plugin_error=None):
493493
return types.SimpleNamespace()
494494

495495
module.build_composer = lambda *, dry_run_only_override=None: FakeComposer()
@@ -555,7 +555,7 @@ def build_rebalance_runtime(self, *, silent_cycle_notifications=False):
555555
observed["silent_cycle_notifications"] = silent_cycle_notifications
556556
return types.SimpleNamespace()
557557

558-
def build_rebalance_config(self, *, strategy_plugin_signals=()):
558+
def build_rebalance_config(self, *, strategy_plugin_signals=(), strategy_plugin_error=None):
559559
return types.SimpleNamespace()
560560

561561
module.build_composer = lambda *, dry_run_only_override=None: observed.__setitem__("override", dry_run_only_override) or FakeComposer()

tests/test_runtime_composer.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ def fake_bootstrap_builder(**kwargs):
7070
strategy_adapters=SimpleNamespace(
7171
calculate_strategy_indicators="strategy-indicators",
7272
resolve_rebalance_plan="resolve-plan",
73+
build_strategy_plugin_notification_lines=lambda signals: tuple(signals),
74+
build_strategy_plugin_error_notification_lines=lambda error: (f"plugin-error:{error}",) if error else (),
7375
),
7476
estimate_max_purchase_quantity_fn="estimate-max-purchase",
7577
fetch_order_status_fn="fetch-order-status",
@@ -106,7 +108,10 @@ def fake_bootstrap_builder(**kwargs):
106108
reporting_adapters = composer.build_reporting_adapters()
107109
runtime = composer.build_rebalance_runtime()
108110
silent_runtime = composer.build_rebalance_runtime(silent_cycle_notifications=True)
109-
config = composer.build_rebalance_config()
111+
config = composer.build_rebalance_config(
112+
strategy_plugin_signals=("plugin-line",),
113+
strategy_plugin_error="bad config",
114+
)
110115

111116
assert notification_adapters.notification_port == "notification-port"
112117
assert reporting_adapters == "reporting-adapters"
@@ -118,6 +123,8 @@ def fake_bootstrap_builder(**kwargs):
118123
assert observed["reporting_builder"]["runtime_assembly"].runtime_target.platform_id == "longbridge"
119124
assert observed["reporting_builder"]["runtime_assembly"].runtime_target.strategy_profile == "soxl_soxx_trend_income"
120125
assert observed["reporting_builder"]["runtime_assembly"].runtime_target.execution_mode == "paper"
126+
assert "plugin-line" in config.extra_notification_lines
127+
assert "plugin-error:bad config" in config.extra_notification_lines
121128
assert observed["bootstrap_builder"]["secret_name"] == "secret-1"
122129
assert observed["bootstrap_builder"]["calculate_strategy_indicators_fn"] == "strategy-indicators"
123130
assert runtime.bootstrap == "bootstrap"

tests/test_runtime_strategy_adapters.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ def fake_load(mounts, *, strategy_profile):
275275
signal_text_fn=lambda icon: f"signal:{icon}",
276276
translator=lambda key, **kwargs: {
277277
"strategy_plugin_line": "plugin={plugin}|mode={mode}|route={route}|action={action}",
278+
"strategy_plugin_error_line": "plugin-error={reason}|fallback=built-in",
279+
"strategy_plugin_error_reason_ValueError": "config validation failed",
278280
"strategy_plugin_name_crisis_response_shadow": "Crisis",
279281
"strategy_plugin_mode_shadow": "shadow",
280282
"strategy_plugin_route_no_action": "no action",
@@ -301,6 +303,9 @@ def fake_load(mounts, *, strategy_profile):
301303
assert adapters.build_strategy_plugin_notification_lines(signals) == (
302304
"plugin=Crisis|mode=shadow|route=no action|action=monitor",
303305
)
306+
assert adapters.build_strategy_plugin_error_notification_lines("ValueError: bad config") == (
307+
"plugin-error=config validation failed|fallback=built-in",
308+
)
304309
assert adapters.build_strategy_plugin_alert_messages(signals) == ()
305310

306311

0 commit comments

Comments
 (0)