4545 required_semiconductor_rotation_history_lookback ,
4646)
4747from quant_platform_kit .common .strategy_plugins import (
48- build_strategy_plugin_alert_messages ,
4948 build_strategy_plugin_notification_lines ,
5049 build_strategy_plugin_report_payload ,
5150 load_configured_strategy_plugin_signals ,
5251 parse_strategy_plugin_mounts ,
5352)
54- from quant_platform_kit .notifications .email import send_smtp_email
5553from quant_platform_kit .notifications .events import NotificationPublisher , RenderedNotification
54+ from quant_platform_kit .notifications .strategy_plugin_email import (
55+ StrategyPluginEmailAlertMarkerStore ,
56+ build_strategy_plugin_alert_context_label as build_email_alert_context_label ,
57+ publish_strategy_plugin_email_alerts ,
58+ )
5659from quant_platform_kit .strategy_contracts import build_strategy_evaluation_inputs
5760from runtime_config_support import PlatformRuntimeSettings , load_platform_runtime_settings
5861from strategy_runtime import load_strategy_runtime
@@ -208,36 +211,30 @@ def attach_strategy_plugin_result(
208211 return result
209212
210213
211- def _call_log_message (log_message : Callable [..., Any ], text : str ) -> None :
212- try :
213- log_message (text , flush = True )
214- except TypeError :
215- log_message (text )
214+ def build_strategy_plugin_alert_context_label (settings : PlatformRuntimeSettings ) -> str :
215+ return build_email_alert_context_label (
216+ platform_id = "firstrade" ,
217+ strategy_profile = settings .strategy_profile ,
218+ account_scope = settings .account_region or settings .account_prefix ,
219+ service_name = settings .account_prefix ,
220+ runtime_target = settings .runtime_target ,
221+ )
216222
217223
218- def send_crisis_alert_email (
219- alert_message ,
220- * ,
224+ def build_strategy_plugin_alert_store (
221225 settings : PlatformRuntimeSettings ,
222- smtp_module = None ,
223- log_message : Callable [..., Any ] = print ,
224- ) -> bool :
225- send_kwargs : dict [str , Any ] = {}
226- if smtp_module is not None :
227- send_kwargs ["smtp_module" ] = smtp_module
228- return send_smtp_email (
229- subject = alert_message .subject ,
230- body = alert_message .body ,
231- smtp_host = getattr (settings , "crisis_alert_smtp_host" , None ),
232- smtp_port = getattr (settings , "crisis_alert_smtp_port" , 587 ),
233- sender = getattr (settings , "crisis_alert_email_from" , None ),
234- recipients = getattr (settings , "crisis_alert_email_to" , ()),
235- username = getattr (settings , "crisis_alert_smtp_username" , None ),
236- password = getattr (settings , "crisis_alert_smtp_password" , None ),
237- use_starttls = getattr (settings , "crisis_alert_smtp_starttls" , True ),
238- use_ssl = getattr (settings , "crisis_alert_smtp_ssl" , False ),
239- printer = lambda text , ** _kwargs : _call_log_message (log_message , text ),
240- ** send_kwargs ,
226+ * ,
227+ env_reader : Callable [[str , str | None ], str | None ] = os .getenv ,
228+ ):
229+ explicit_gcs_uri = env_reader ("STRATEGY_PLUGIN_ALERT_STATE_GCS_URI" , None )
230+ report_gcs_uri = env_reader ("EXECUTION_REPORT_GCS_URI" , None )
231+ state_bucket = env_reader ("FIRSTRADE_GCS_STATE_BUCKET" , None )
232+ state_prefix = env_reader ("FIRSTRADE_STATE_PREFIX" , "firstrade-platform" ) or "firstrade-platform"
233+ state_gcs_uri = f"gs://{ state_bucket } /{ state_prefix } " if state_bucket else None
234+ return StrategyPluginEmailAlertMarkerStore (
235+ local_dir = env_reader ("STRATEGY_PLUGIN_ALERT_STATE_DIR" , None ) or "/tmp/quant_strategy_plugin_alerts" ,
236+ gcs_prefix_uri = explicit_gcs_uri or report_gcs_uri or state_gcs_uri ,
237+ gcp_project_id = settings .project_id ,
241238 )
242239
243240
@@ -247,22 +244,17 @@ def publish_strategy_plugin_alerts(
247244 settings : PlatformRuntimeSettings ,
248245 translator : Callable [..., str ],
249246 log_message : Callable [..., Any ] = print ,
250- ) -> int :
251- sent_count = 0
252- for alert_message in build_strategy_plugin_alert_messages (
247+ env_reader : Callable [[ str , str | None ], str | None ] = os . getenv ,
248+ ):
249+ return publish_strategy_plugin_email_alerts (
253250 signals ,
251+ email_settings = settings ,
254252 translator = translator ,
255253 strategy_label = settings .strategy_profile ,
256- ):
257- if send_crisis_alert_email (
258- alert_message ,
259- settings = settings ,
260- log_message = log_message ,
261- ):
262- sent_count += 1
263- if sent_count :
264- _call_log_message (log_message , f"strategy_plugin_alert_email_sent count={ sent_count } " )
265- return sent_count
254+ context_label = build_strategy_plugin_alert_context_label (settings ),
255+ alert_store = build_strategy_plugin_alert_store (settings , env_reader = env_reader ),
256+ log_message = log_message ,
257+ )
266258
267259
268260def _runtime_metadata_with_execution_policy (
@@ -388,21 +380,26 @@ def run_strategy_cycle(
388380 }
389381 ],
390382 "action_done" : False ,
383+ "strategy_plugin_alert_email_attempted_count" : 0 ,
391384 "strategy_plugin_alert_email_sent_count" : 0 ,
385+ "strategy_plugin_alert_email_skipped_count" : 0 ,
386+ "strategy_plugin_alert_email_failed_count" : 0 ,
387+ "strategy_plugin_alert_email_deliveries" : [],
392388 }
393389 return attach_strategy_plugin_result (
394390 result ,
395391 signals = strategy_plugin_signals ,
396392 error = strategy_plugin_error ,
397393 translator = translator ,
398394 )
399- strategy_plugin_alert_email_sent_count = 0
395+ strategy_plugin_alert_email_result = None
400396 strategy_plugin_alert_email_error = None
401397 try :
402- strategy_plugin_alert_email_sent_count = publish_strategy_plugin_alerts (
398+ strategy_plugin_alert_email_result = publish_strategy_plugin_alerts (
403399 strategy_plugin_signals ,
404400 settings = settings ,
405401 translator = translator ,
402+ env_reader = env_reader ,
406403 )
407404 except Exception as exc :
408405 strategy_plugin_alert_email_error = f"{ type (exc ).__name__ } : { exc } "
@@ -482,7 +479,18 @@ def run_strategy_cycle(
482479 result ["funding_blocked" ] = True
483480 if strategy_run_persistence_error :
484481 result ["strategy_run_persistence_error" ] = strategy_run_persistence_error
485- result ["strategy_plugin_alert_email_sent_count" ] = strategy_plugin_alert_email_sent_count
482+ if strategy_plugin_alert_email_result is not None :
483+ result .update (strategy_plugin_alert_email_result .to_report_fields ())
484+ else :
485+ result .update (
486+ {
487+ "strategy_plugin_alert_email_attempted_count" : 0 ,
488+ "strategy_plugin_alert_email_sent_count" : 0 ,
489+ "strategy_plugin_alert_email_skipped_count" : 0 ,
490+ "strategy_plugin_alert_email_failed_count" : 0 ,
491+ "strategy_plugin_alert_email_deliveries" : [],
492+ }
493+ )
486494 if strategy_plugin_alert_email_error :
487495 result ["strategy_plugin_alert_email_error" ] = strategy_plugin_alert_email_error
488496 attach_strategy_plugin_result (
0 commit comments