2121from application .cycle_result import coerce_strategy_cycle_result
2222from application .runtime_broker_adapters import build_runtime_broker_adapters
2323from application .runtime_composer import build_runtime_composer
24- from application .runtime_deadline import RuntimeDeadlineExceeded , enforce_runtime_deadline
24+ from application .runtime_deadline import (
25+ RuntimeDeadlineExceeded ,
26+ enforce_runtime_deadline ,
27+ recycle_current_process_after_response ,
28+ )
2529from application .runtime_strategy_adapters import (
2630 build_runtime_strategy_adapters ,
2731 fetch_yfinance_historical_candles ,
@@ -1113,6 +1117,7 @@ def _handle_request(
11131117 )
11141118 execution_window = "dry_run" if dry_run_only_override else "execution"
11151119 lock_acquired = STRATEGY_RUN_LOCK .acquire (blocking = False )
1120+ deadline_exceeded = False
11161121 try :
11171122 log_runtime_event (
11181123 log_context ,
@@ -1200,18 +1205,14 @@ def _handle_request(
12001205 if dry_run_only_override is None :
12011206 publish_strategy_plugin_alerts (strategy_plugin_signals , report = report )
12021207 notification_delivery_events : list [dict ] = []
1203- with enforce_runtime_deadline (
1204- IBKR_DRY_RUN_DEADLINE_SECONDS if dry_run_only_override is True else 0 ,
1205- operation = "IBKR strategy dry-run" ,
1206- ):
1207- cycle_result = coerce_strategy_cycle_result (
1208- run_strategy_core (
1209- strategy_plugin_signals = strategy_plugin_signals ,
1210- strategy_plugin_error = strategy_plugin_error ,
1211- dry_run_only_override = dry_run_only_override ,
1212- notification_delivery_events = notification_delivery_events ,
1213- )
1208+ cycle_result = coerce_strategy_cycle_result (
1209+ run_strategy_core (
1210+ strategy_plugin_signals = strategy_plugin_signals ,
1211+ strategy_plugin_error = strategy_plugin_error ,
1212+ dry_run_only_override = dry_run_only_override ,
1213+ notification_delivery_events = notification_delivery_events ,
12141214 )
1215+ )
12151216 execution_summary = dict (cycle_result .execution_summary or {})
12161217 reconciliation_record = dict (cycle_result .reconciliation_record or {})
12171218 signal_metadata = dict (cycle_result .signal_metadata or {})
@@ -1283,30 +1284,9 @@ def _handle_request(
12831284 result = cycle_result .result ,
12841285 )
12851286 return (response_body if dry_run_only_override else cycle_result .result ), 200
1286- except RuntimeDeadlineExceeded as exc :
1287- append_runtime_report_error (
1288- report ,
1289- stage = "strategy_deadline" ,
1290- message = str (exc ),
1291- error_type = type (exc ).__name__ ,
1292- )
1293- finalize_runtime_report (report , status = "error" )
1294- log_runtime_event (
1295- log_context ,
1296- "strategy_cycle_deadline_exceeded" ,
1297- message = "Strategy dry-run exceeded its application deadline" ,
1298- severity = "ERROR" ,
1299- execution_window = execution_window ,
1300- timeout_seconds = IBKR_DRY_RUN_DEADLINE_SECONDS ,
1301- error_message = str (exc ),
1302- )
1303- error_msg = f"🚨 【IBKR 运行超时】\n { str (exc )} "
1304- _publish_runtime_failure_notification (
1305- detailed_text = error_msg ,
1306- compact_text = error_msg ,
1307- exc = exc ,
1308- )
1309- return "Error" , 503
1287+ except RuntimeDeadlineExceeded :
1288+ deadline_exceeded = True
1289+ raise
13101290 except TimeoutError as exc :
13111291 append_runtime_report_error (
13121292 report ,
@@ -1356,14 +1336,15 @@ def _handle_request(
13561336 finally :
13571337 if lock_acquired :
13581338 STRATEGY_RUN_LOCK .release ()
1359- try :
1360- if dry_run_only_override is None :
1361- report_path = persist_execution_report (report )
1362- else :
1363- report_path = persist_execution_report (report , dry_run_only_override = dry_run_only_override )
1364- print (f"execution_report { report_path } " , flush = True )
1365- except Exception as persist_exc :
1366- print (f"failed to persist execution report: { persist_exc } " , flush = True )
1339+ if not deadline_exceeded :
1340+ try :
1341+ if dry_run_only_override is None :
1342+ report_path = persist_execution_report (report )
1343+ else :
1344+ report_path = persist_execution_report (report , dry_run_only_override = dry_run_only_override )
1345+ print (f"execution_report { report_path } " , flush = True )
1346+ except Exception as persist_exc :
1347+ print (f"failed to persist execution report: { persist_exc } " , flush = True )
13671348
13681349
13691350def _handle_probe (* , response_body : str = "Probe OK" ):
@@ -1507,11 +1488,7 @@ def _dispatch_local_monitor(dispatch):
15071488 if window == "probe" :
15081489 _body , status_code = _handle_probe ()
15091490 elif window == "precheck" :
1510- _body , status_code = _handle_request (
1511- dry_run_only_override = True ,
1512- response_body = "Dry Run OK" ,
1513- dry_run_label = "strategy dry-run" ,
1514- )
1491+ _body , status_code = _handle_dry_run_with_deadline ()
15151492 else :
15161493 raise ValueError (f"Unsupported local monitor window: { window !r} " )
15171494 return {"status_code" : int (status_code )}
@@ -1522,14 +1499,42 @@ def handle_request():
15221499 return _route_with_runtime_error_fallback (_handle_request )
15231500
15241501
1502+ def _handle_dry_run_with_deadline ():
1503+ try :
1504+ with enforce_runtime_deadline (
1505+ IBKR_DRY_RUN_DEADLINE_SECONDS ,
1506+ operation = "IBKR strategy dry-run request" ,
1507+ ):
1508+ return _route_with_runtime_error_fallback (
1509+ _handle_request ,
1510+ dry_run_only_override = True ,
1511+ response_body = "Dry Run OK" ,
1512+ dry_run_label = "strategy dry-run" ,
1513+ )
1514+ except RuntimeDeadlineExceeded as exc :
1515+ print (
1516+ json .dumps (
1517+ {
1518+ "severity" : "ERROR" ,
1519+ "event" : "strategy_cycle_deadline_exceeded" ,
1520+ "message" : str (exc ),
1521+ "service_name" : SERVICE_NAME or os .getenv ("K_SERVICE" ),
1522+ "strategy_profile" : STRATEGY_PROFILE ,
1523+ "timeout_seconds" : IBKR_DRY_RUN_DEADLINE_SECONDS ,
1524+ "worker_recycle_scheduled" : True ,
1525+ },
1526+ ensure_ascii = False ,
1527+ separators = ("," , ":" ),
1528+ ),
1529+ flush = True ,
1530+ )
1531+ recycle_current_process_after_response ()
1532+ return "Error" , 503
1533+
1534+
15251535@app .route ("/dry-run" , methods = ["POST" , "GET" ])
15261536def handle_dry_run ():
1527- return _route_with_runtime_error_fallback (
1528- _handle_request ,
1529- dry_run_only_override = True ,
1530- response_body = "Dry Run OK" ,
1531- dry_run_label = "strategy dry-run" ,
1532- )
1537+ return _handle_dry_run_with_deadline ()
15331538
15341539
15351540@app .route ("/probe" , methods = ["POST" , "GET" ])
0 commit comments