|
25 | 25 | from decision_mapper import map_strategy_decision |
26 | 26 | from entrypoints.cloud_run import is_market_open_today |
27 | 27 | from notifications.telegram import build_strategy_display_name, build_translator, send_telegram_message |
28 | | -from quant_platform_kit.notifications.email import send_smtp_email |
| 28 | +from quant_platform_kit.notifications.strategy_plugin_email import ( |
| 29 | + StrategyPluginEmailAlertMarkerStore, |
| 30 | + build_strategy_plugin_alert_context_label as build_email_alert_context_label, |
| 31 | + publish_strategy_plugin_email_alerts, |
| 32 | +) |
29 | 33 | from quant_platform_kit.common.runtime_assembly import build_runtime_assembly |
30 | 34 | from quant_platform_kit.common.runtime_reports import ( |
31 | 35 | append_runtime_report_error, |
@@ -233,14 +237,6 @@ def _env_flag(name: str) -> bool: |
233 | 237 | TG_TOKEN = RUNTIME_SETTINGS.tg_token |
234 | 238 | TG_CHAT_ID = RUNTIME_SETTINGS.tg_chat_id |
235 | 239 | NOTIFY_LANG = RUNTIME_SETTINGS.notify_lang |
236 | | -CRISIS_ALERT_EMAIL_TO = getattr(RUNTIME_SETTINGS, "crisis_alert_email_to", ()) |
237 | | -CRISIS_ALERT_EMAIL_FROM = getattr(RUNTIME_SETTINGS, "crisis_alert_email_from", None) |
238 | | -CRISIS_ALERT_SMTP_HOST = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_host", None) |
239 | | -CRISIS_ALERT_SMTP_PORT = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_port", 587) |
240 | | -CRISIS_ALERT_SMTP_USERNAME = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_username", None) |
241 | | -CRISIS_ALERT_SMTP_PASSWORD = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_password", None) |
242 | | -CRISIS_ALERT_SMTP_STARTTLS = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_starttls", True) |
243 | | -CRISIS_ALERT_SMTP_SSL = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_ssl", False) |
244 | 240 |
|
245 | 241 | CASH_RESERVE_RATIO = STRATEGY_RUNTIME.cash_reserve_ratio |
246 | 242 | CASH_RESERVE_FLOOR_USD = getattr(STRATEGY_RUNTIME, "cash_reserve_floor_usd", 0.0) |
@@ -481,29 +477,42 @@ def build_strategy_plugin_alert_messages(signals): |
481 | 477 | return build_strategy_adapters().build_strategy_plugin_alert_messages(signals) |
482 | 478 |
|
483 | 479 |
|
484 | | -def send_crisis_alert_email(alert_message) -> bool: |
485 | | - return send_smtp_email( |
486 | | - subject=alert_message.subject, |
487 | | - body=alert_message.body, |
488 | | - smtp_host=CRISIS_ALERT_SMTP_HOST, |
489 | | - smtp_port=CRISIS_ALERT_SMTP_PORT, |
490 | | - sender=CRISIS_ALERT_EMAIL_FROM, |
491 | | - recipients=CRISIS_ALERT_EMAIL_TO, |
492 | | - username=CRISIS_ALERT_SMTP_USERNAME, |
493 | | - password=CRISIS_ALERT_SMTP_PASSWORD, |
494 | | - use_starttls=CRISIS_ALERT_SMTP_STARTTLS, |
495 | | - use_ssl=CRISIS_ALERT_SMTP_SSL, |
| 480 | +def build_strategy_plugin_alert_store(): |
| 481 | + return StrategyPluginEmailAlertMarkerStore( |
| 482 | + local_dir=os.getenv("STRATEGY_PLUGIN_ALERT_STATE_DIR") or "/tmp/quant_strategy_plugin_alerts", |
| 483 | + gcs_prefix_uri=os.getenv("STRATEGY_PLUGIN_ALERT_STATE_GCS_URI") or os.getenv("EXECUTION_REPORT_GCS_URI"), |
| 484 | + gcp_project_id=PROJECT_ID, |
496 | 485 | ) |
497 | 486 |
|
498 | 487 |
|
499 | | -def publish_strategy_plugin_alerts(signals) -> int: |
500 | | - sent_count = 0 |
501 | | - for alert_message in build_strategy_plugin_alert_messages(signals): |
502 | | - if send_crisis_alert_email(alert_message): |
503 | | - sent_count += 1 |
504 | | - if sent_count: |
505 | | - print(f"strategy_plugin_alert_email_sent count={sent_count}", flush=True) |
506 | | - return sent_count |
| 488 | +def build_strategy_plugin_alert_context_label() -> str: |
| 489 | + return build_email_alert_context_label( |
| 490 | + platform_id="ibkr", |
| 491 | + strategy_profile=STRATEGY_PROFILE, |
| 492 | + account_scope=ACCOUNT_GROUP, |
| 493 | + service_name=SERVICE_NAME, |
| 494 | + runtime_target=RUNTIME_SETTINGS.runtime_target, |
| 495 | + ) |
| 496 | + |
| 497 | + |
| 498 | +def attach_strategy_plugin_alert_email_result(report, result) -> None: |
| 499 | + report.setdefault("summary", {})["strategy_plugin_alert_email_sent_count"] = result.sent_count |
| 500 | + report.setdefault("diagnostics", {}).update(result.to_report_fields()) |
| 501 | + |
| 502 | + |
| 503 | +def publish_strategy_plugin_alerts(signals, *, report=None): |
| 504 | + result = publish_strategy_plugin_email_alerts( |
| 505 | + signals, |
| 506 | + email_settings=RUNTIME_SETTINGS, |
| 507 | + translator=t, |
| 508 | + strategy_label=STRATEGY_PROFILE, |
| 509 | + context_label=build_strategy_plugin_alert_context_label(), |
| 510 | + alert_store=build_strategy_plugin_alert_store(), |
| 511 | + log_message=print, |
| 512 | + ) |
| 513 | + if report is not None: |
| 514 | + attach_strategy_plugin_alert_email_result(report, result) |
| 515 | + return result |
507 | 516 |
|
508 | 517 |
|
509 | 518 | def build_account_notification_lines() -> tuple[str, ...]: |
@@ -640,7 +649,7 @@ def _handle_request(*, dry_run_only_override: bool | None = None, response_body: |
640 | 649 | execution_window="precheck" if dry_run_only_override else "execution", |
641 | 650 | ) |
642 | 651 | if dry_run_only_override is None: |
643 | | - publish_strategy_plugin_alerts(strategy_plugin_signals) |
| 652 | + publish_strategy_plugin_alerts(strategy_plugin_signals, report=report) |
644 | 653 | cycle_result = coerce_strategy_cycle_result( |
645 | 654 | run_strategy_core( |
646 | 655 | strategy_plugin_signals=strategy_plugin_signals, |
|
0 commit comments