|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +import json |
| 4 | + |
| 5 | +from application.paper_execution_admission import ( |
| 6 | + PAPER_ADMISSION_ENABLED_ENV, |
| 7 | + PAPER_EXECUTION_COMMAND_ENV, |
| 8 | + evaluate_paper_dry_run_admission, |
| 9 | +) |
| 10 | +from quant_platform_kit.common.execution_commands import ExecutionCommand |
| 11 | +from quant_platform_kit.common.paper_execution_admission import build_paper_risk_admission_receipt |
| 12 | +from quant_platform_kit.common.runtime_target import build_runtime_target |
| 13 | + |
| 14 | + |
| 15 | +def _release() -> dict[str, str]: |
| 16 | + return { |
| 17 | + "release_id": "soxl-p2-v3.20260825", |
| 18 | + "manifest_sha256": "a" * 64, |
| 19 | + "strategy_revision": "soxl-p2-v3", |
| 20 | + "config_sha256": "b" * 64, |
| 21 | + "risk_policy_sha256": "c" * 64, |
| 22 | + "evidence_sha256": "d" * 64, |
| 23 | + "plugin_bundle_sha256": "e" * 64, |
| 24 | + "effective_session": "2026-08-25", |
| 25 | + } |
| 26 | + |
| 27 | + |
| 28 | +def _runtime_target(): |
| 29 | + return build_runtime_target( |
| 30 | + platform_id="firstrade", |
| 31 | + strategy_profile="soxl_soxx_trend_income", |
| 32 | + dry_run_only=True, |
| 33 | + strategy_release=_release(), |
| 34 | + ) |
| 35 | + |
| 36 | + |
| 37 | +def _command( |
| 38 | + *, |
| 39 | + platform: str = "firstrade", |
| 40 | + include_receipt: bool = True, |
| 41 | + disposition: str = "allow_new_risk", |
| 42 | + reason_codes: tuple[str, ...] = (), |
| 43 | + command_decision_digest: str = "f" * 64, |
| 44 | +) -> dict[str, object]: |
| 45 | + release = _release() |
| 46 | + intent: dict[str, object] = {"strategy_release": release, "targets": {"SOXL": 0.2}} |
| 47 | + if include_receipt: |
| 48 | + receipt = build_paper_risk_admission_receipt( |
| 49 | + strategy_profile="soxl_soxx_trend_income", |
| 50 | + release_id=release["release_id"], |
| 51 | + risk_policy_sha256=release["risk_policy_sha256"], |
| 52 | + decision_digest="f" * 64, |
| 53 | + effective_session="2026-08-25", |
| 54 | + disposition=disposition, |
| 55 | + reason_codes=reason_codes, |
| 56 | + ) |
| 57 | + intent["paper_risk_admission_receipt"] = receipt.to_dict() |
| 58 | + return ExecutionCommand.from_decision( |
| 59 | + platform=platform, |
| 60 | + account_scope="paper", |
| 61 | + strategy_profile="soxl_soxx_trend_income", |
| 62 | + execution_mode="paper", |
| 63 | + signal_date="2026-08-24", |
| 64 | + effective_date="2026-08-25", |
| 65 | + execution_timing_contract="next_trading_day", |
| 66 | + decision_digest=command_decision_digest, |
| 67 | + intent=intent, |
| 68 | + created_at="2026-08-24T20:00:00+00:00", |
| 69 | + ).to_dict() |
| 70 | + |
| 71 | + |
| 72 | +def test_paper_admission_is_disabled_by_default_even_without_a_command(): |
| 73 | + assert evaluate_paper_dry_run_admission(runtime_target=None, env={}) is None |
| 74 | + |
| 75 | + |
| 76 | +def test_paper_admission_requires_an_immutable_command_before_preview(): |
| 77 | + audit = evaluate_paper_dry_run_admission( |
| 78 | + runtime_target=_runtime_target(), |
| 79 | + env={PAPER_ADMISSION_ENABLED_ENV: "true"}, |
| 80 | + ) |
| 81 | + |
| 82 | + assert audit == { |
| 83 | + "schema_version": "firstrade_paper_execution_admission_audit.v1", |
| 84 | + "admission_enabled": True, |
| 85 | + "audit_color": "red", |
| 86 | + "status": "blocked", |
| 87 | + "command_id": None, |
| 88 | + "disposition": "halted", |
| 89 | + "integrity_findings": ["paper_execution_command_missing"], |
| 90 | + "receipt_sha256": None, |
| 91 | + } |
| 92 | + |
| 93 | + |
| 94 | +def test_matching_command_receipt_and_runtime_release_admit_paper_preview(): |
| 95 | + audit = evaluate_paper_dry_run_admission( |
| 96 | + runtime_target=_runtime_target(), |
| 97 | + env={ |
| 98 | + PAPER_ADMISSION_ENABLED_ENV: "true", |
| 99 | + PAPER_EXECUTION_COMMAND_ENV: json.dumps(_command()), |
| 100 | + }, |
| 101 | + ) |
| 102 | + |
| 103 | + assert audit is not None |
| 104 | + assert audit["status"] == "admitted" |
| 105 | + assert audit["audit_color"] == "green" |
| 106 | + assert audit["disposition"] == "allow_new_risk" |
| 107 | + assert audit["integrity_findings"] == [] |
| 108 | + |
| 109 | + |
| 110 | +def test_non_firstrade_or_missing_embedded_receipt_stays_redacted_and_blocked(): |
| 111 | + for command, finding in ( |
| 112 | + (_command(platform="longbridge"), "paper_execution_platform_mismatch"), |
| 113 | + (_command(include_receipt=False), "paper_risk_admission_receipt_missing"), |
| 114 | + ): |
| 115 | + audit = evaluate_paper_dry_run_admission( |
| 116 | + runtime_target=_runtime_target(), |
| 117 | + env={ |
| 118 | + PAPER_ADMISSION_ENABLED_ENV: "true", |
| 119 | + PAPER_EXECUTION_COMMAND_ENV: json.dumps(command), |
| 120 | + }, |
| 121 | + ) |
| 122 | + |
| 123 | + assert audit is not None |
| 124 | + assert audit["status"] == "blocked" |
| 125 | + assert audit["audit_color"] == "red" |
| 126 | + assert audit["integrity_findings"] == [finding] |
| 127 | + assert "targets" not in audit |
| 128 | + assert "account_scope" not in audit |
| 129 | + |
| 130 | + |
| 131 | +def test_reducing_only_receipt_is_blocked_until_a_platform_can_prove_reduction(): |
| 132 | + audit = evaluate_paper_dry_run_admission( |
| 133 | + runtime_target=_runtime_target(), |
| 134 | + env={ |
| 135 | + PAPER_ADMISSION_ENABLED_ENV: "true", |
| 136 | + PAPER_EXECUTION_COMMAND_ENV: json.dumps( |
| 137 | + _command( |
| 138 | + disposition="reducing_only", |
| 139 | + reason_codes=("DAILY_LOSS_LIMIT_EXCEEDED",), |
| 140 | + ) |
| 141 | + ), |
| 142 | + }, |
| 143 | + ) |
| 144 | + |
| 145 | + assert audit is not None |
| 146 | + assert audit["status"] == "blocked" |
| 147 | + assert audit["audit_color"] == "red" |
| 148 | + assert audit["disposition"] == "reducing_only" |
| 149 | + assert audit["integrity_findings"] == ["paper_risk_admission_reducing_only"] |
| 150 | + |
| 151 | + |
| 152 | +def test_mismatched_risk_decision_digest_is_red_and_never_admitted(): |
| 153 | + audit = evaluate_paper_dry_run_admission( |
| 154 | + runtime_target=_runtime_target(), |
| 155 | + env={ |
| 156 | + PAPER_ADMISSION_ENABLED_ENV: "true", |
| 157 | + PAPER_EXECUTION_COMMAND_ENV: json.dumps(_command(command_decision_digest="e" * 64)), |
| 158 | + }, |
| 159 | + ) |
| 160 | + |
| 161 | + assert audit is not None |
| 162 | + assert audit["status"] == "blocked" |
| 163 | + assert audit["audit_color"] == "red" |
| 164 | + assert audit["disposition"] == "halted" |
| 165 | + assert audit["integrity_findings"] == ["paper_risk_admission_command_mismatch"] |
0 commit comments