From 864bf24280545eabca3204d8dc74c99f6fdf5556 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Sat, 26 Sep 2026 04:27:59 +0800 Subject: [PATCH] Add bounded LongBridge Phase 5 offline conversion Co-Authored-By: Codex --- research/PHASE5_OFFLINE_ADAPTER.md | 114 +++++++++++++ research/phase5_offline_adapter.py | 231 +++++++++++++++++++++++++++ tests/test_phase5_offline_adapter.py | 214 +++++++++++++++++++++++++ 3 files changed, 559 insertions(+) create mode 100644 research/PHASE5_OFFLINE_ADAPTER.md create mode 100644 research/phase5_offline_adapter.py create mode 100644 tests/test_phase5_offline_adapter.py diff --git a/research/PHASE5_OFFLINE_ADAPTER.md b/research/PHASE5_OFFLINE_ADAPTER.md new file mode 100644 index 00000000..a9b57045 --- /dev/null +++ b/research/PHASE5_OFFLINE_ADAPTER.md @@ -0,0 +1,114 @@ +# Phase 5 LongBridge offline research adapter + +## Frozen scope + +This slice compares the already frozen Phase 4 v3 QQQM/TQQQ/BOXX research +candidate with an isolated LongBridge offline representation. It does not +change LongBridge production execution, account, market-data, release, risk, +workflow or broker submission behavior. It does not grant paper, shadow or live +authority. + +The exact write whitelist is: + +- `research/phase5_offline_adapter.py`: a pure, unregistered research adapter; +- `tests/test_phase5_offline_adapter.py`: synthetic and approved-local-input checks; +- `research/PHASE5_OFFLINE_ADAPTER.md`: this scope and observed evidence. + +The branch starts at LongBridgePlatform `origin/main` +`aa8432bd6d3c8dd8ed3a441241a146dd0ff1ca30`. Its locked shared package +refs are QPK `c7646a7168b3dafa763ef7751a182d23e8de7790` and UES +`8412d8338879f335ef8830678c2733e02e53e7c7`. Neither includes the +unmerged frozen Phase 4 v3 research candidate. The authorized research source +remains the separate UES worktree at +`aa555fa7cf04d85805de6301de468884755ccdde`, with Phase 4 v3 policy +SHA256 `95d19f727a5e7cae1d9ab5a4251d260acca0f5370ef075c85312a06887e2e20e` +and approved input manifest SHA256 +`cb14a511083c824a748d137a271c93cfe0e8adf38f648905b26e37decf4c6182`. + +The original LongBridge worktree has unrelated uncommitted changes and is +untouched. Inspected workflows run CI on pull requests; deployment and paper +candidate application require explicit dispatch, and automatic merge applies +only to eligible non-draft Dependabot pull requests. This branch will remain a +draft research PR. + +## Starting diagnostic + +The existing pure LongBridge paper mapper at +`application/paper_execution_command_consumer.py` had source SHA256 +`71a6297f2dd0de678fa2e66c1b7ced3ee8983536643a6a15c28c96d29f56fe18`. +On the eight SHA-checked frozen 10 bps first-open paths, its value-delta +quantity matched no complete research whole-share set; all eight paths had at +least one fractional proposal, and one proposed a positive quantity where the +research ledger bought zero shares. No account, command consumer, execution +cycle or broker port was called. This diagnoses the pure mapper, not an actual +LongBridge fill or rejection. + +The default native execution path also has distinct small-account bootstrap, +safe-haven cash substitution and limit-price funding rules. Those rules are +not implicitly changed to fit the frozen research candidate. The bounded +offline adapter must preserve the original research targets, member ownership, +whole-share quantities, costs and funding state separately from native order +semantics; unsupported states must remain explicit. + +## Implemented conversion contract + +`convert_funded_phase5_proposal(plan, funded)` accepts the frozen prior-close +plan and an already computed, open-price funded research proposal. It verifies +candidate and budget-policy identity, signal and execution dates, whole-share +targets, member ownership, buy order, filled quantities, cost at the declared +5/10/15 bps scenario, available settled cash, TQQQ member reserve, post-open +shares and cash, and the account wealth identity. It independently recomputes +the first-open funding bound from the supplied plan; it does not generate the +strategy target or run the historical core. The proposal cannot simply assert +success: altered trades, costs, cash, reserves, ownership or dates are rejected. + +The output keeps the frozen USD and share targets, the explicitly unfilled +shares, research costs and simulated account state separate from a LongBridge +offline row. Each row has the `.US` platform symbol, buy side, positive whole +share quantity and `quantity_step=1`, open-price reference, proposed before and +after shares, and exposure direction. This row is not a native LongBridge +order, limit-price decision or broker fill. Zero-share targets produce no row. + +This slice supports the cash-only first open with no existing holdings, sale +queue, receivables, option/collateral obligations or external flows. Other +states fail explicitly. It does not claim sale, T+2, dividend, corporate-action +reconstruction, full-window or production-platform equivalence. The verified +proposal is supplied separately; this adapter must not call the existing +fractional value-target mapper to recalculate it. + +## Direct results + +The approved local private source and daily ledgers were reused without new +acquisition. The eight predeclared first-open paths at 10 bps comprise four +principal scales and the enhanced/matched-defense pair. The test consumed the +reviewed Schwab pure research funding projection as an upstream calculation, +then independently checked its frozen plan and converted the result. All eight +LongBridge offline share sets matched the separately saved research ledger; +settled cash, executed cost and member reserve matched at `1e-6` USD +tolerance. One funded shortfall remained explicit. This is conversion fidelity +plus direct first-open accounting verification, not an independent rerun of the +funding strategy or a native LongBridge execution test. + +Sixteen synthetic/negative direct test cases passed alongside the eight-path +opt-in private test. The cases cover repeated pure evaluation, source mutation +protection, whole-share output, zero-share omission, and rejection of altered +cost, post-state, reserve, owner, status, target or time. The ordinary CI test +skips the private case. No raw private prices, source rows or ledgers are part +of the branch or public PR artifacts. + +Astra's preimplementation review recommended this bounded plan-plus-funded +proposal conversion instead of copying a third complete research funding +engine. It required independent verification of the original plan, arithmetic +and account state rather than trusting a success label. Final implementation +review and PR/CI evidence will be recorded after completion. + +## Incremental independent review + +Astra reviewed the three implemented files against the frozen research funding +contract and returned GO for this bounded offline conversion. It found no +blocking mismatch in the first-open buy order, whole shares, executed-cost +accounting, restricted cash, member reserve, account identity, candidate or +time checks. The review used source and synthetic evidence only; it did not +read or independently rerun private daily data. The eight-path conversion +result above comes from the direct approved-local test. Native LongBridge +execution, complete history and production authorization remain unverified. diff --git a/research/phase5_offline_adapter.py b/research/phase5_offline_adapter.py new file mode 100644 index 00000000..7631ded6 --- /dev/null +++ b/research/phase5_offline_adapter.py @@ -0,0 +1,231 @@ +"""Verify and represent a frozen Phase 5 funding proposal for offline LongBridge research.""" + +from __future__ import annotations + +import math +from datetime import date +from typing import Any, Mapping + +SYMBOLS = ("TQQQ", "QQQM", "BOXX") +OWNERS = {"TQQQ": "tqqq_core", "QQQM": "outer", "BOXX": "outer"} +CANDIDATE_IDS = frozenset({ + "qqqm_tqqq_guard_boxx_fixed_research_v3", + "qqqm_boxx_matched_defense_research_v3", + "qqqm_tqqq_guard_boxx_fixed_scale_research_v1", + "qqqm_boxx_matched_defense_scale_research_v1", +}) +POLICY_VERSION = "fixed_45_05_49_01_vs_50_00_49_01_restricted_paid_cash_v3" +TOLERANCE_USD = 1e-6 + + +def _number(value: Any, field: str) -> float: + if isinstance(value, bool): + raise ValueError(f"{field}: invalid number") + try: + number = float(value) + except (TypeError, ValueError) as exc: + raise ValueError(f"{field}: invalid number") from exc + if not math.isfinite(number) or number < 0: + raise ValueError(f"{field}: must be finite and nonnegative") + return number + + +def _integer(value: Any, field: str) -> int: + number = _number(value, field) + if number != int(number): + raise ValueError(f"{field}: whole shares required") + return int(number) + + +def _same_money(actual: Any, expected: float, field: str) -> None: + if abs(_number(actual, field) - expected) > TOLERANCE_USD: + raise ValueError(f"{field}: funded proposal mismatch") + + +def _same_symbols(actual: Mapping[str, Any], expected: Mapping[str, int], field: str) -> None: + if set(actual) != set(SYMBOLS) or any( + _integer(actual[symbol], f"{field}.{symbol}") != expected[symbol] + for symbol in SYMBOLS + ): + raise ValueError(f"{field}: funded proposal mismatch") + + +def convert_funded_phase5_proposal(plan: Mapping[str, Any], funded: Mapping[str, Any]) -> dict: + """Check a frozen funded research step before making LongBridge-shaped evidence. + + No broker object, market-data port, release receipt, or order object is + accepted. The returned rows are offline research representations only. + """ + if plan.get("candidate_id") not in CANDIDATE_IDS or plan.get("budget_policy_version") != POLICY_VERSION: + raise ValueError("PHASE5_IDENTITY") + if funded.get("candidate_id") != plan["candidate_id"]: + raise ValueError("PHASE5_FUNDED_IDENTITY") + signal = date.fromisoformat(str(plan["signal_date"])) + execution = date.fromisoformat(str(plan["execution_date"])) + quote = date.fromisoformat(str(plan["quote_date"])) + if not signal < execution or quote != execution: + raise ValueError("PHASE5_TIME_CAUSALITY") + if funded.get("signal_date") != signal.isoformat() or funded.get("execution_date") != execution.isoformat(): + raise ValueError("PHASE5_FUNDED_TIME") + if plan.get("company_actions_applied") is not True: + raise ValueError("PHASE5_COMPANY_ACTIONS") + if funded.get("scope") != "offline_research_open_price_simulation_only": + raise ValueError("PHASE5_FUNDED_SCOPE") + if funded.get("status") not in {"FEASIBLE_RESEARCH_PROPOSAL", "FUNDED_SHORTFALL"}: + raise ValueError("PHASE5_FUNDED_STATUS") + if type(plan.get("session_index")) is not int or plan["session_index"] < 0: + raise ValueError("PHASE5_SESSION_INDEX") + for field in ("option_liability_usd", "collateral_usd", "external_cash_flow_usd"): + if _number(plan.get(field, 0), field): + raise ValueError(f"PHASE5_UNSUPPORTED_{field.upper()}") + + prices = {symbol: _number(plan["open_prices_usd"][symbol], f"open_prices_usd.{symbol}") + for symbol in SYMBOLS} + if set(plan["open_prices_usd"]) != set(SYMBOLS) or any(not price for price in prices.values()): + raise ValueError("PHASE5_PRICES") + if set(plan["account_shares"]) != set(SYMBOLS): + raise ValueError("PHASE5_ACCOUNT_SYMBOLS") + before = {symbol: _integer(plan["account_shares"][symbol], f"account_shares.{symbol}") + for symbol in SYMBOLS} + targets_by_owner = plan["target_usd_by_owner"] + positions_by_owner = plan["positions_shares_by_owner"] + if set(targets_by_owner) != set(OWNERS.values()) or set(positions_by_owner) != set(OWNERS.values()): + raise ValueError("PHASE5_OWNER_SET") + targets = {} + target_shares = {} + for owner in ("tqqq_core", "outer"): + if set(targets_by_owner[owner]) - set(SYMBOLS) or set(positions_by_owner[owner]) - set(SYMBOLS): + raise ValueError("PHASE5_OWNER_SYMBOLS") + targets[owner] = {symbol: _number(targets_by_owner[owner].get(symbol, 0), + f"targets.{owner}.{symbol}") for symbol in SYMBOLS} + target_shares[owner] = {symbol: math.floor(targets[owner][symbol] / prices[symbol]) + for symbol in SYMBOLS} + for symbol in SYMBOLS: + member_before = _integer(positions_by_owner[owner].get(symbol, 0), + f"positions.{owner}.{symbol}") + if owner != OWNERS[symbol] and (member_before or targets[owner][symbol]): + raise ValueError("PHASE5_UNSUPPORTED_MEMBER_OWNERSHIP") + if any(before[symbol] != sum(_integer(positions_by_owner[owner].get(symbol, 0), + f"positions.{owner}.{symbol}") + for owner in ("tqqq_core", "outer")) for symbol in SYMBOLS): + raise ValueError("PHASE5_MEMBER_ACCOUNT_IDENTITY") + if funded.get("target_usd_by_owner") != targets or funded.get("target_shares_by_owner") != target_shares: + raise ValueError("PHASE5_FUNDED_TARGET") + total_target = {symbol: sum(target_shares[owner][symbol] for owner in ("tqqq_core", "outer")) + for symbol in SYMBOLS} + fee_bps = _number(plan["cost_bps_per_side"], "cost_bps_per_side") + if fee_bps not in {5, 10, 15}: + raise ValueError("PHASE5_UNSUPPORTED_COST") + rate = fee_bps / 10_000.0 + budget = _number(plan["tqqq_member_budget_usd"], "tqqq_member_budget_usd") + cash_target = _number(plan["outer_cash_target_usd"], "outer_cash_target_usd") + if targets["tqqq_core"]["TQQQ"] > budget + TOLERANCE_USD: + raise ValueError("PHASE5_MEMBER_TARGET_BUDGET") + cash = _number(plan["settled_cash_usd"], "settled_cash_usd") + restricted = _number(plan["restricted_paid_cash_usd"], "restricted_paid_cash_usd") + if restricted > cash + TOLERANCE_USD: + raise ValueError("PHASE5_RESTRICTED_CASH") + if plan["pending_sales"] or plan["unpaid_receivables"]: + raise ValueError("PHASE5_UNSUPPORTED_PENDING_OR_RECEIVABLE") + if any(before.values()): + raise ValueError("PHASE5_UNSUPPORTED_EXISTING_HOLDINGS") + + # This first-open slice verifies a previously funded proposal. It does not + # recreate the strategy's budget or state machine. + expected_rows = [] + shares = dict(before) + costs = {symbol: 0.0 for symbol in SYMBOLS} + shortfalls = {} + for symbol in SYMBOLS: + gap = total_target[symbol] - shares[symbol] + reserve = 0.0 if symbol == "TQQQ" else max(0.0, budget - shares["TQQQ"] * prices["TQQQ"]) + available = max(0.0, cash - restricted - cash_target - reserve) + affordable = math.floor((available + 1e-9) / (prices[symbol] * (1.0 + rate))) + fill = min(gap, affordable) + if fill: + cost = fill * prices[symbol] * rate + expected_rows.append({"symbol": symbol, "side": "buy", "shares": fill, + "owner": OWNERS[symbol], "open_price_usd": prices[symbol], + "research_cost_usd": cost}) + cash -= fill * prices[symbol] + cost + costs[symbol] = cost + shares[symbol] += fill + if fill < gap: + shortfalls[symbol] = {"target_shares": total_target[symbol], + "proposed_shares": shares[symbol], + "reason": "insufficient_free_settled_cash"} + actual_rows = tuple(funded["simulated_trades"]) + if len(actual_rows) != len(expected_rows): + raise ValueError("PHASE5_FUNDED_TRADES") + for actual, expected in zip(actual_rows, expected_rows, strict=True): + if type(actual.get("shares")) is not int or actual["shares"] <= 0: + raise ValueError("PHASE5_FUNDED_WHOLE_SHARES") + if set(actual) != set(expected) or any( + actual[key] != expected[key] for key in ("symbol", "side", "shares", "owner") + ): + raise ValueError("PHASE5_FUNDED_TRADES") + _same_money(actual["open_price_usd"], expected["open_price_usd"], "trade.open_price_usd") + _same_money(actual["research_cost_usd"], expected["research_cost_usd"], + "trade.research_cost_usd") + if funded.get("unfilled_target_shares") != shortfalls or funded["status"] != ( + "FUNDED_SHORTFALL" if shortfalls else "FEASIBLE_RESEARCH_PROPOSAL" + ): + raise ValueError("PHASE5_FUNDED_SHORTFALL") + state = funded["simulated_post_open_state"] + _same_symbols(state["account_shares"], shares, "post.account_shares") + for owner in ("tqqq_core", "outer"): + expected_member = {symbol: shares[symbol] if OWNERS[symbol] == owner else 0 + for symbol in SYMBOLS} + _same_symbols(state["positions_shares_by_owner"][owner], expected_member, + f"post.positions.{owner}") + if set(state["positions_shares_by_owner"]) != {"tqqq_core", "outer"}: + raise ValueError("PHASE5_FUNDED_OWNER_SET") + if state["pending_sales"] or state["unpaid_receivables"]: + raise ValueError("PHASE5_FUNDED_UNSUPPORTED_STATE") + _same_money(state["settled_cash_usd"], cash, "post.settled_cash_usd") + _same_money(state["restricted_paid_cash_usd"], restricted, "post.restricted_paid_cash_usd") + if set(funded["net_account_proposed_deltas"]) != set(SYMBOLS) or any( + _integer(funded["net_account_proposed_deltas"][symbol], f"delta.{symbol}") != shares[symbol] + for symbol in SYMBOLS + ): + raise ValueError("PHASE5_FUNDED_ACCOUNT_DELTA") + if set(funded["research_cost_usd_by_symbol"]) != set(SYMBOLS): + raise ValueError("PHASE5_FUNDED_COST_SYMBOLS") + for symbol in SYMBOLS: + _same_money(funded["research_cost_usd_by_symbol"][symbol], costs[symbol], f"cost.{symbol}") + total_cost = math.fsum(costs.values()) + _same_money(funded["research_total_cost_usd"], total_cost, "research_total_cost_usd") + reserve_after = max(0.0, budget - shares["TQQQ"] * prices["TQQQ"]) + _same_money(funded["member_reserved_cash_after_usd"], reserve_after, + "member_reserved_cash_after_usd") + _same_money(funded["outer_free_settled_cash_after_usd"], + max(0.0, cash - restricted - reserve_after), "outer_free_settled_cash_after_usd") + prior_nav = _number(plan["settled_cash_usd"], "settled_cash_usd") + after_nav = cash + math.fsum(shares[symbol] * prices[symbol] for symbol in SYMBOLS) + _same_money(funded["pre_open_account_nav_usd"], prior_nav, "pre_open_account_nav_usd") + _same_money(funded["simulated_post_open_account_nav_usd"], after_nav, + "simulated_post_open_account_nav_usd") + identity_error = abs(prior_nav - total_cost - after_nav) + _same_money(funded["account_identity_error_usd"], identity_error, + "account_identity_error_usd") + _same_money(funded["released_pending_sale_usd"], 0.0, "released_pending_sale_usd") + if identity_error > TOLERANCE_USD: + raise ValueError("PHASE5_ACCOUNT_IDENTITY") + + rows = tuple({"symbol": row["symbol"], "platform_symbol": f"{row['symbol']}.US", + "side": "buy", "quantity": row["shares"], "quantity_step": 1, + "owner": row["owner"], "reference_open_price_usd": row["open_price_usd"], + "reference_notional_usd": row["shares"] * row["open_price_usd"], + "research_cost_usd": row["research_cost_usd"], + "before_shares": before[row["symbol"]], "proposed_after_shares": shares[row["symbol"]], + "exposure_effect": "increases"} for row in expected_rows) + return {"status": funded["status"], "candidate_id": plan["candidate_id"], + "signal_date": signal.isoformat(), "execution_date": execution.isoformat(), + "target_usd_by_owner": targets, "target_shares_by_owner": target_shares, + "offline_proposals": rows, "unfilled_target_shares": shortfalls, + "research_total_cost_usd": total_cost, + "simulated_post_open_state": {"account_shares": shares, "settled_cash_usd": cash, + "member_reserved_cash_usd": reserve_after, + "restricted_paid_cash_usd": restricted}, + "account_identity_error_usd": identity_error, + "scope": "longbridge_offline_research_conversion_only"} diff --git a/tests/test_phase5_offline_adapter.py b/tests/test_phase5_offline_adapter.py new file mode 100644 index 00000000..c60be2bd --- /dev/null +++ b/tests/test_phase5_offline_adapter.py @@ -0,0 +1,214 @@ +"""Direct checks for the bounded LongBridge Phase 5 offline research conversion.""" + +from __future__ import annotations + +import copy +import importlib.util +import json +import os +import sys +from pathlib import Path + +import pytest + +from research.phase5_offline_adapter import POLICY_VERSION, convert_funded_phase5_proposal + + +def _plan(**changes): + plan = { + "candidate_id": "qqqm_tqqq_guard_boxx_fixed_research_v3", + "budget_policy_version": POLICY_VERSION, + "signal_date": "2023-03-27", "execution_date": "2023-03-28", + "quote_date": "2023-03-28", "session_index": 0, + "company_actions_applied": True, + "open_prices_usd": {"TQQQ": 100, "QQQM": 50, "BOXX": 100}, + "target_usd_by_owner": {"tqqq_core": {"TQQQ": 200}, + "outer": {"QQQM": 250, "BOXX": 300}}, + "positions_shares_by_owner": {"tqqq_core": {}, "outer": {}}, + "account_shares": {"TQQQ": 0, "QQQM": 0, "BOXX": 0}, + "settled_cash_usd": 1000, "restricted_paid_cash_usd": 0, + "pending_sales": [], "unpaid_receivables": [], + "tqqq_member_budget_usd": 300, "outer_cash_target_usd": 10, + "cost_bps_per_side": 10, + "option_liability_usd": 0, "collateral_usd": 0, + "external_cash_flow_usd": 0, + } + plan.update(changes) + return plan + + +def _funded(): + trades = ( + {"symbol": "TQQQ", "side": "buy", "shares": 2, "owner": "tqqq_core", + "open_price_usd": 100.0, "research_cost_usd": 0.2}, + {"symbol": "QQQM", "side": "buy", "shares": 5, "owner": "outer", + "open_price_usd": 50.0, "research_cost_usd": 0.25}, + {"symbol": "BOXX", "side": "buy", "shares": 3, "owner": "outer", + "open_price_usd": 100.0, "research_cost_usd": 0.3}, + ) + return { + "status": "FEASIBLE_RESEARCH_PROPOSAL", + "candidate_id": "qqqm_tqqq_guard_boxx_fixed_research_v3", + "signal_date": "2023-03-27", "execution_date": "2023-03-28", + "target_usd_by_owner": {"tqqq_core": {"TQQQ": 200.0, "QQQM": 0.0, "BOXX": 0.0}, + "outer": {"TQQQ": 0.0, "QQQM": 250.0, "BOXX": 300.0}}, + "target_shares_by_owner": {"tqqq_core": {"TQQQ": 2, "QQQM": 0, "BOXX": 0}, + "outer": {"TQQQ": 0, "QQQM": 5, "BOXX": 3}}, + "simulated_trades": trades, "unfilled_target_shares": {}, + "research_cost_usd_by_symbol": {"TQQQ": 0.2, "QQQM": 0.25, "BOXX": 0.3}, + "research_total_cost_usd": 0.75, + "net_account_proposed_deltas": {"TQQQ": 2, "QQQM": 5, "BOXX": 3}, + "member_reserved_cash_after_usd": 100.0, + "outer_free_settled_cash_after_usd": 149.25, + "released_pending_sale_usd": 0.0, + "pre_open_account_nav_usd": 1000.0, + "simulated_post_open_account_nav_usd": 999.25, + "account_identity_error_usd": 0.0, + "simulated_post_open_state": { + "account_shares": {"TQQQ": 2, "QQQM": 5, "BOXX": 3}, + "positions_shares_by_owner": { + "tqqq_core": {"TQQQ": 2, "QQQM": 0, "BOXX": 0}, + "outer": {"TQQQ": 0, "QQQM": 5, "BOXX": 3}}, + "settled_cash_usd": 249.25, "restricted_paid_cash_usd": 0.0, + "pending_sales": (), "unpaid_receivables": (), + }, + "scope": "offline_research_open_price_simulation_only", + } + + +def test_whole_share_longbridge_representation_and_account_identity(): + plan, funded = _plan(), _funded() + before = copy.deepcopy((plan, funded)) + first = convert_funded_phase5_proposal(plan, funded) + assert first == convert_funded_phase5_proposal(plan, funded) + assert (plan, funded) == before + assert first["status"] == "FEASIBLE_RESEARCH_PROPOSAL" + assert [row["platform_symbol"] for row in first["offline_proposals"]] == [ + "TQQQ.US", "QQQM.US", "BOXX.US"] + assert [row["quantity"] for row in first["offline_proposals"]] == [2, 5, 3] + assert all(row["quantity_step"] == 1 and row["exposure_effect"] == "increases" + for row in first["offline_proposals"]) + assert first["simulated_post_open_state"]["settled_cash_usd"] == pytest.approx(249.25) + assert first["simulated_post_open_state"]["member_reserved_cash_usd"] == 100 + assert first["account_identity_error_usd"] == 0 + + +@pytest.mark.parametrize("path,value", [ + (("simulated_trades", 0, "shares"), 2.5), + (("simulated_trades", 0, "owner"), "outer"), + (("simulated_trades", 0, "open_price_usd"), 101), + (("simulated_trades", 0, "research_cost_usd"), 0), + (("simulated_trades", 0, "side"), "sell"), + (("simulated_post_open_state", "settled_cash_usd"), 250), + (("simulated_post_open_state", "account_shares", "BOXX"), 4), + (("research_total_cost_usd",), 0), + (("member_reserved_cash_after_usd",), 0), + (("unfilled_target_shares",), {"BOXX": {"target_shares": 3}}), + (("status",), "FUNDED_SHORTFALL"), + (("candidate_id",), "wrong"), + (("signal_date",), "2023-03-28"), +]) +def test_rejects_tampered_funded_proposal(path, value): + funded = copy.deepcopy(_funded()) + target = funded + for key in path[:-1]: + target = target[key] + target[path[-1]] = value + with pytest.raises((ValueError, TypeError)): + convert_funded_phase5_proposal(_plan(), funded) + + +def test_rejects_unsupported_or_noncausal_input(): + funded = _funded() + variants = ( + _plan(quote_date="2023-03-29"), + _plan(open_prices_usd={"TQQQ": 101, "QQQM": 50, "BOXX": 100}), + _plan(account_shares={"TQQQ": 1, "QQQM": 0, "BOXX": 0}), + _plan(pending_sales=[{"symbol": "TQQQ", "net_amount_usd": 100, + "release_session_index": 2}]), + _plan(unpaid_receivables=[{"symbol": "QQQM", "amount_usd": 1}]), + _plan(option_liability_usd=1), + _plan(target_usd_by_owner={"tqqq_core": {"TQQQ": 200}, + "outer": {"TQQQ": 1, "QQQM": 250, "BOXX": 300}}), + ) + for plan in variants: + with pytest.raises((ValueError, TypeError)): + convert_funded_phase5_proposal(plan, funded) + + +def test_zero_share_and_funded_shortfall_are_explicit(): + plan = _plan(target_usd_by_owner={"tqqq_core": {"TQQQ": 0}, + "outer": {"QQQM": 250, "BOXX": 300}}) + funded = copy.deepcopy(_funded()) + funded["target_usd_by_owner"]["tqqq_core"]["TQQQ"] = 0.0 + funded["target_shares_by_owner"]["tqqq_core"]["TQQQ"] = 0 + funded["simulated_trades"] = funded["simulated_trades"][1:] + funded["research_cost_usd_by_symbol"]["TQQQ"] = 0.0 + funded["research_total_cost_usd"] = 0.55 + funded["net_account_proposed_deltas"]["TQQQ"] = 0 + funded["member_reserved_cash_after_usd"] = 300.0 + funded["outer_free_settled_cash_after_usd"] = 149.45 + funded["simulated_post_open_state"]["account_shares"]["TQQQ"] = 0 + funded["simulated_post_open_state"]["positions_shares_by_owner"]["tqqq_core"]["TQQQ"] = 0 + funded["simulated_post_open_state"]["settled_cash_usd"] = 449.45 + funded["simulated_post_open_account_nav_usd"] = 999.45 + result = convert_funded_phase5_proposal(plan, funded) + assert [row["symbol"] for row in result["offline_proposals"]] == ["QQQM", "BOXX"] + + +@pytest.mark.skipif(not all(os.environ.get(name) for name in ( + "QSL_PHASE5_PRIVATE_ROOT", "QSL_PHASE5_UES_ROOT", "QSL_PHASE5_SCHWAB_ROOT")), + reason="approved local frozen research inputs not attached") +def test_eight_frozen_first_open_paths_preserve_funded_ledger(): + private_root = Path(os.environ["QSL_PHASE5_PRIVATE_ROOT"]) + ues_root = Path(os.environ["QSL_PHASE5_UES_ROOT"]) + schwab_root = Path(os.environ["QSL_PHASE5_SCHWAB_ROOT"]) + sys.path.insert(0, str(ues_root / "docs/research/first_compounding_20260925")) + try: + from boxx_outer_cash_compare import _load + from phase5_locked_inventory_audit import (COST_BPS, PATHS, PRINCIPALS, + SCALE_SUMMARY_SHA, _ledger, _sha) + finally: + sys.path.pop(0) + source = schwab_root / "research/phase5_offline_adapter.py" + spec = importlib.util.spec_from_file_location("schwab_phase5_frozen_funding", source) + upstream = importlib.util.module_from_spec(spec) + spec.loader.exec_module(upstream) + summary_path = private_root / "phase5_capital_scale_v1/phase5_capital_scale_summary.v1.json" + assert _sha(summary_path) == SCALE_SUMMARY_SHA + study = json.loads(summary_path.read_text()) + _, _, _, rows, _ = _load(private_root) + first = next(row for row in rows if row["date"] == "2023-03-28") + prices = {symbol: float(first[symbol.lower() + "_open"]) + for symbol in ("TQQQ", "QQQM", "BOXX")} + count = shortfall_count = 0 + for principal in PRINCIPALS: + for path in PATHS: + day = _ledger(private_root, principal, path, study)[0] + target = day["target_usd"] + plan = _plan(candidate_id=day["candidate_id"], + signal_date=day["signal_date"], execution_date=day["date"], + quote_date=day["date"], open_prices_usd=prices, + target_usd_by_owner={"tqqq_core": {"TQQQ": target["TQQQ"]}, + "outer": {"QQQM": target["QQQM"], + "BOXX": target["BOXX"]}}, + settled_cash_usd=principal, + tqqq_member_budget_usd=day["member_budget_usd"], + outer_cash_target_usd=day["cash_target_usd"], + cost_bps_per_side=COST_BPS) + funded = upstream.adapt_phase5_first_platform_offline(plan) + result = convert_funded_phase5_proposal(plan, funded) + assert {row["symbol"]: row["quantity"] for row in result["offline_proposals"]} == { + symbol: int(day["trade_shares"][symbol]) for symbol in ("TQQQ", "QQQM", "BOXX") + if int(day["trade_shares"][symbol]) > 0} + assert result["simulated_post_open_state"]["settled_cash_usd"] == pytest.approx( + day["settled_cash_usd"], abs=1e-6) + assert result["research_total_cost_usd"] == pytest.approx( + sum(day["trade_cost_usd"].values()), abs=1e-6) + assert result["simulated_post_open_state"]["member_reserved_cash_usd"] == pytest.approx( + day["member_reserved_cash_at_open_usd"], abs=1e-6) + shortfall_count += result["status"] == "FUNDED_SHORTFALL" + count += 1 + assert count == 8 and shortfall_count == 1 + print(f"longbridge_first_open_paths={count} funded_shortfalls={shortfall_count} " + "share_cash_cost_reserve_mismatches=0")