99from application .execution_state import build_execution_marker_key
1010from application .durable_execution_commands import enqueue_paper_execution_command
1111from application .runtime_dependencies import LongBridgeRebalanceConfig , LongBridgeRebalanceRuntime
12+ from quant_platform_kit .common .account_identity import (
13+ AccountIdentityGuardedExecutionPort ,
14+ AccountIdentityPolicy ,
15+ evaluate_account_identity ,
16+ )
1217from quant_platform_kit .longbridge .market_data import fetch_lot_sizes
1318from application .signal_snapshot import build_signal_snapshot
1419from notifications .events import NotificationPublisher
@@ -240,6 +245,37 @@ def _durable_command_required_message(*, execution: dict) -> str:
240245 )
241246
242247
248+ def _account_identity_blocked_message (* , findings : tuple [str , ...]) -> str :
249+ detail = ", " .join (findings ) or "account_identity_blocked"
250+ return f"Broker orders blocked by account identity gate: { detail } "
251+
252+
253+ def _evaluate_execution_account_identity (
254+ * ,
255+ runtime : LongBridgeRebalanceRuntime ,
256+ config : LongBridgeRebalanceConfig ,
257+ trade_context ,
258+ ):
259+ policy = getattr (config , "account_identity_policy" , None )
260+ if policy is None :
261+ policy = AccountIdentityPolicy ()
262+ if not isinstance (policy , AccountIdentityPolicy ):
263+ policy = AccountIdentityPolicy .from_mapping (policy )
264+ if not policy .is_configured :
265+ return None
266+ observer = getattr (runtime , "account_identity_observer" , None )
267+ observation = observer (trade_context ) if callable (observer ) else None
268+ return evaluate_account_identity (
269+ expected_platform_id = getattr (
270+ config ,
271+ "account_identity_expected_platform_id" ,
272+ "longbridge" ,
273+ ),
274+ policy = policy ,
275+ observation = observation ,
276+ )
277+
278+
243279def _should_record_execution_marker (* , result : ExecutionCycleResult , config : LongBridgeRebalanceConfig ) -> bool :
244280 if not getattr (config , "execution_dedup_enabled" , False ):
245281 return False
@@ -293,6 +329,16 @@ def run_strategy(
293329 quote_context , trade_context , indicators = runtime .bootstrap ()
294330 market_data_port = runtime .market_data_port_factory (quote_context )
295331 execution_port = runtime .execution_port_factory (trade_context )
332+ account_identity_decision = _evaluate_execution_account_identity (
333+ runtime = runtime ,
334+ config = config ,
335+ trade_context = trade_context ,
336+ )
337+ if account_identity_decision is not None :
338+ execution_port = AccountIdentityGuardedExecutionPort (
339+ delegate = execution_port ,
340+ decision = account_identity_decision ,
341+ )
296342
297343 def load_plan (* , current_snapshot ):
298344 current_snapshot = attach_strategy_plugin_metadata (
@@ -318,6 +364,24 @@ def fetch_replanned_state():
318364 return load_plan (current_snapshot = current_snapshot )
319365
320366 plan , portfolio , execution , allocation = fetch_replanned_state ()
367+ account_identity_blocked = bool (
368+ account_identity_decision is not None
369+ and not account_identity_decision .broker_write_allowed
370+ )
371+ if account_identity_decision is not None :
372+ execution ["account_identity" ] = account_identity_decision .to_receipt ()
373+ if account_identity_decision .would_block :
374+ print (
375+ config .with_prefix (
376+ "account_identity_gate "
377+ f"enforcement={ account_identity_decision .policy .enforcement .value } "
378+ f"findings={ ',' .join (account_identity_decision .findings )} "
379+ ),
380+ flush = True ,
381+ )
382+ if account_identity_blocked :
383+ execution ["account_identity_blocked" ] = True
384+ execution ["account_identity_block_reason" ] = "account_identity_verification_failed"
321385 paper_command_observation = enqueue_paper_execution_command (
322386 enabled = bool (getattr (config , "durable_execution_command_paper_enabled" , False )),
323387 dry_run_only = bool (getattr (config , "dry_run_only" , False )),
@@ -342,7 +406,7 @@ def fetch_replanned_state():
342406 if direct_live_routing_blocked :
343407 execution ["direct_live_routing_blocked" ] = True
344408 execution ["direct_live_routing_block_reason" ] = "durable_execution_command_required"
345- execution_already_recorded = direct_live_routing_blocked
409+ execution_already_recorded = direct_live_routing_blocked or account_identity_blocked
346410 execution_claim_acquired = False
347411 if not direct_live_routing_blocked and execution_marker_key and execution_state_store :
348412 try :
@@ -390,7 +454,12 @@ def fetch_replanned_state():
390454 ) from exc
391455
392456 if execution_already_recorded :
393- if direct_live_routing_blocked :
457+ if account_identity_blocked :
458+ message = _account_identity_blocked_message (
459+ findings = tuple (account_identity_decision .findings ),
460+ )
461+ runtime .notify_issue ("Account identity gate blocked broker orders" , message )
462+ elif direct_live_routing_blocked :
394463 message = _durable_command_required_message (execution = execution )
395464 runtime .notify_issue ("Next-session execution blocked" , message )
396465 else :
0 commit comments