import hashlib import time from typing import List from dataclasses import dataclass from .primitives import LocalArbProblem, SharedSignals @dataclass class PlanDelta: legs: List[dict] total_size: float delta_id: str # Additional provenance fields to support deterministic replay and auditing timestamp: float | None = None parent_delta_id: str | None = None signature: str | None = None def _deterministic_id(inputs: List[LocalArbProblem], shared: SharedSignals) -> str: # Create a stable representation of inputs for deterministic hashing parts = [] for lp in sorted(inputs, key=lambda x: (x.asset_pair[0], x.asset_pair[1])): parts.append( f"{lp.asset_pair}:{lp.target_mispricing}:{lp.liquidity_budget}:{lp.latency_budget}" ) s = "|".join(parts) + f"|shared:{shared.cross_venue_corr}:{shared.latency_proxy}" return hashlib.sha256(s.encode("utf-8")).hexdigest() def admm_lite_step(local_problems: List[LocalArbProblem], shared_signals: SharedSignals) -> PlanDelta: # Deterministic, simple projection: create one leg per local problem legs = [] # deterministic order by asset_pair for lp in sorted(local_problems, key=lambda x: (x.asset_pair[0], x.asset_pair[1])): leg_size = max(0.0, float(lp.liquidity_budget) * 0.01) legs.append({ "asset_pair": list(lp.asset_pair), "size": leg_size, "venue": "default", }) total_size = float(sum(lp.liquidity_budget for lp in local_problems)) * 0.01 if local_problems else 0.0 delta_id = _deterministic_id(local_problems, shared_signals) # Attach provenance metadata for replay and auditing return PlanDelta( legs=legs, total_size=total_size, delta_id=delta_id, timestamp=time.time(), parent_delta_id=None, signature=None, )