1616
1717from quant_platform_kit .common .execution_commands import (
1818 ExecutionCommand ,
19- ExecutionCommandState ,
2019 ExecutionCommandStore ,
21- validate_execution_command_release_binding ,
2220)
23- from quant_platform_kit .common .paper_execution_admission import evaluate_paper_execution_admission
21+ from quant_platform_kit .common .paper_execution_command_consumer import (
22+ PaperExecutionProposal ,
23+ PaperExecutionReconciliation ,
24+ consume_due_paper_execution_commands as consume_shared_paper_execution_commands ,
25+ )
2426from quant_platform_kit .common .runtime_command_gate import (
25- RuntimeCommandAction ,
2627 RuntimeCommandExposureEffect ,
27- RuntimeCommandGateEnforcement ,
28- RuntimeCommandGatePolicy ,
29- evaluate_runtime_command_gate ,
30- )
31- from quant_platform_kit .common .strategy_release import (
32- StrategyReleaseIdentity ,
33- build_strategy_release_identity ,
34- validate_runtime_loaded_receipt ,
3528)
29+ from quant_platform_kit .common .strategy_release import StrategyReleaseIdentity
3630
37- PAPER_COMMAND_CONSUMER_SCHEMA_VERSION = "longbridge.paper-execution-command-consumer.v1"
3831PAPER_EXECUTION_INTENT_SCHEMA_VERSION = "longbridge.paper-execution-intent.v1"
3932_NOTIONAL_TOLERANCE = 0.01
40- _PAPER_COMMAND_GATE_POLICY = RuntimeCommandGatePolicy (
41- enforcement = RuntimeCommandGateEnforcement .ENFORCE ,
42- )
4333
4434
4535def _normalized_symbol (value : object ) -> str :
@@ -184,53 +174,34 @@ def _build_reconciled_order_proposals(
184174 return tuple (proposals ), tuple (dict .fromkeys (findings ))
185175
186176
187- def _append_or_raise (
188- store : ExecutionCommandStore ,
177+ def _reconcile_command (
189178 command : ExecutionCommand ,
190179 * ,
191- next_state : ExecutionCommandState ,
192- expected_previous_state : ExecutionCommandState ,
193- details : Mapping [str , object ],
194- ) -> None :
195- event = store .append_event (
180+ portfolio : Any ,
181+ market_data_port : Any ,
182+ ) -> PaperExecutionReconciliation :
183+ """Adapt LongBridge's value-target evidence to the shared paper contract."""
184+
185+ proposals , integrity_findings = _build_reconciled_order_proposals (
196186 command ,
197- next_state = next_state ,
198- expected_previous_state = expected_previous_state ,
199- details = details ,
187+ portfolio = portfolio ,
188+ market_data_port = market_data_port ,
189+ )
190+ return PaperExecutionReconciliation (
191+ proposals = tuple (
192+ PaperExecutionProposal (
193+ symbol = str (proposal ["symbol" ]),
194+ exposure_effect = str (proposal ["exposure_effect" ]),
195+ details = {
196+ key : value
197+ for key , value in proposal .items ()
198+ if key not in {"symbol" , "exposure_effect" }
199+ },
200+ )
201+ for proposal in proposals
202+ ),
203+ integrity_findings = integrity_findings ,
200204 )
201- if event is None :
202- raise RuntimeError (f"failed to persist paper command event { next_state .value } " )
203-
204-
205- def _attempt_reconciliation_required (
206- store : ExecutionCommandStore ,
207- command : ExecutionCommand ,
208- * ,
209- error : Exception ,
210- ) -> None :
211- try :
212- state = store .current_state (command )
213- if state not in {
214- ExecutionCommandState .CLAIMED ,
215- ExecutionCommandState .SUBMITTED ,
216- ExecutionCommandState .ACCEPTED ,
217- ExecutionCommandState .PARTIALLY_FILLED ,
218- }:
219- return
220- store .append_event (
221- command ,
222- next_state = ExecutionCommandState .RECONCILIATION_REQUIRED ,
223- expected_previous_state = state ,
224- details = {
225- "paper_simulation" : True ,
226- "reason" : "consumer_exception_requires_manual_reconciliation" ,
227- "error_type" : type (error ).__name__ ,
228- },
229- )
230- except Exception :
231- # The original error is already captured by the caller's result. Do
232- # not risk masking it with a second storage failure.
233- return
234205
235206
236207def consume_due_paper_execution_commands (
@@ -243,164 +214,17 @@ def consume_due_paper_execution_commands(
243214 runtime_release_receipt : Mapping [str , Any ] | None ,
244215 expected_strategy_release : StrategyReleaseIdentity | Mapping [str , object ] | None ,
245216) -> dict [str , object ]:
246- """Claim and simulate due paper commands; never submit a broker order."""
247- if store is None or (not store .cloud_prefix_uri and not store .local_dir ):
248- raise RuntimeError ("paper durable execution command store is required" )
249- try :
250- expected_release = build_strategy_release_identity (expected_strategy_release )
251- except ValueError :
252- return {
253- "schema_version" : PAPER_COMMAND_CONSUMER_SCHEMA_VERSION ,
254- "status" : "blocked" ,
255- "reason" : "release_identity_invalid" ,
256- "commands" : [],
257- }
258- release_preflight = validate_runtime_loaded_receipt (
259- runtime_release_receipt ,
260- expected_strategy_release = expected_release ,
261- )
262- if not release_preflight .is_valid :
263- return {
264- "schema_version" : PAPER_COMMAND_CONSUMER_SCHEMA_VERSION ,
265- "status" : "blocked" ,
266- "reason" : release_preflight .findings [0 ],
267- "commands" : [],
268- }
269-
270- as_of_date = str (as_of_session )[:10 ]
271- commands : list [dict [str , object ]] = []
272- for command in store .list_due (as_of_date ):
273- if store .current_state (command ) is not ExecutionCommandState .QUEUED :
274- continue
275- claim = store .claim_due (command , as_of_date = as_of_date , claimant = claimant )
276- if claim is None :
277- continue
278- try :
279- admission = evaluate_paper_execution_admission (
280- command = command ,
281- expected_strategy_release = expected_release ,
282- )
283- integrity_findings = list (admission .integrity_findings )
284- integrity_findings .extend (
285- validate_execution_command_release_binding (
286- command ,
287- expected_strategy_release = expected_release ,
288- ).findings
289- )
290- if command .execution_mode != "paper" :
291- integrity_findings .append ("durable_event_history_invalid" )
292- proposals , reconciliation_findings = _build_reconciled_order_proposals (
293- command ,
294- portfolio = portfolio ,
295- market_data_port = market_data_port ,
296- )
297- integrity_findings .extend (reconciliation_findings )
298- integrity_findings = list (dict .fromkeys (integrity_findings ))
299- receipts : list [dict [str , object ]] = []
300- for proposal in proposals :
301- decision = evaluate_runtime_command_gate (
302- action = RuntimeCommandAction .SUBMIT ,
303- exposure_effect = proposal ["exposure_effect" ],
304- command = command ,
305- command_state = ExecutionCommandState .CLAIMED ,
306- as_of_session = as_of_date ,
307- runtime_release_receipt = runtime_release_receipt ,
308- expected_strategy_release = expected_release ,
309- integrity_findings = integrity_findings ,
310- policy = _PAPER_COMMAND_GATE_POLICY ,
311- )
312- receipts .append (decision .to_receipt ())
313-
314- # A no-op command still has to pass the command-level release and
315- # timing checks before it can be closed as paper-filled.
316- if not proposals :
317- decision = evaluate_runtime_command_gate (
318- action = RuntimeCommandAction .SUBMIT ,
319- exposure_effect = RuntimeCommandExposureEffect .NEUTRAL ,
320- command = command ,
321- command_state = ExecutionCommandState .CLAIMED ,
322- as_of_session = as_of_date ,
323- runtime_release_receipt = runtime_release_receipt ,
324- expected_strategy_release = expected_release ,
325- integrity_findings = integrity_findings ,
326- policy = _PAPER_COMMAND_GATE_POLICY ,
327- )
328- receipts .append (decision .to_receipt ())
329-
330- details = {
331- "paper_simulation" : True ,
332- "claimant" : claimant ,
333- "paper_execution_admission" : {
334- "disposition" : admission .disposition .value ,
335- "receipt_sha256" : admission .receipt_sha256 ,
336- },
337- "integrity_findings" : integrity_findings ,
338- "proposals" : list (proposals ),
339- "runtime_command_gate_receipts" : receipts ,
340- }
341- if any (not bool (receipt ["policy_allows" ]) for receipt in receipts ):
342- _append_or_raise (
343- store ,
344- command ,
345- next_state = ExecutionCommandState .REJECTED ,
346- expected_previous_state = ExecutionCommandState .CLAIMED ,
347- details = {
348- ** details ,
349- "reason" : "paper_command_gate_would_block" ,
350- },
351- )
352- commands .append (
353- {
354- "command_id" : command .command_id ,
355- "status" : ExecutionCommandState .REJECTED .value ,
356- "proposals_count" : len (proposals ),
357- "would_block" : True ,
358- }
359- )
360- continue
361-
362- _append_or_raise (
363- store ,
364- command ,
365- next_state = ExecutionCommandState .SUBMITTED ,
366- expected_previous_state = ExecutionCommandState .CLAIMED ,
367- details = details ,
368- )
369- _append_or_raise (
370- store ,
371- command ,
372- next_state = ExecutionCommandState .ACCEPTED ,
373- expected_previous_state = ExecutionCommandState .SUBMITTED ,
374- details = {"paper_simulation" : True , "proposals_count" : len (proposals )},
375- )
376- _append_or_raise (
377- store ,
378- command ,
379- next_state = ExecutionCommandState .FILLED ,
380- expected_previous_state = ExecutionCommandState .ACCEPTED ,
381- details = {"paper_simulation" : True , "simulated_fill_count" : len (proposals )},
382- )
383- commands .append (
384- {
385- "command_id" : command .command_id ,
386- "status" : ExecutionCommandState .FILLED .value ,
387- "proposals_count" : len (proposals ),
388- "would_block" : False ,
389- }
390- )
391- except Exception as exc :
392- _attempt_reconciliation_required (store , command , error = exc )
393- commands .append (
394- {
395- "command_id" : command .command_id ,
396- "status" : ExecutionCommandState .RECONCILIATION_REQUIRED .value ,
397- "error_type" : type (exc ).__name__ ,
398- }
399- )
217+ """Claim and simulate due paper commands through the shared lifecycle."""
400218
401- return {
402- "schema_version" : PAPER_COMMAND_CONSUMER_SCHEMA_VERSION ,
403- "status" : "ok" ,
404- "as_of_session" : as_of_date ,
405- "commands" : commands ,
406- }
219+ return consume_shared_paper_execution_commands (
220+ store = store ,
221+ as_of_session = as_of_session ,
222+ claimant = claimant ,
223+ reconcile_command = lambda command : _reconcile_command (
224+ command ,
225+ portfolio = portfolio ,
226+ market_data_port = market_data_port ,
227+ ),
228+ runtime_release_receipt = runtime_release_receipt ,
229+ expected_strategy_release = expected_strategy_release ,
230+ )
0 commit comments