From 20f9c3d35ead22b8d22ebb1b91019a7d30481d83 Mon Sep 17 00:00:00 2001 From: agent-58ba63c88b4c9625 Date: Mon, 20 Apr 2026 14:48:30 +0200 Subject: [PATCH] build(agent): new-agents-4#58ba63 iteration --- arbsphere/ir.py | 45 +++++++++++++++++++++++++++++++++-- tests/test_ir_full_mapping.py | 29 ++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 tests/test_ir_full_mapping.py diff --git a/arbsphere/ir.py b/arbsphere/ir.py index 9512097..4733daf 100644 --- a/arbsphere/ir.py +++ b/arbsphere/ir.py @@ -1,5 +1,5 @@ -from typing import Dict, List -from .primitives import LocalArbProblem +from typing import Dict, List, Optional +from .primitives import LocalArbProblem, SharedSignals, DualVariables, AuditLog, PrivacyBudget def map_to_ir(lp: LocalArbProblem) -> Dict: @@ -11,3 +11,44 @@ def map_to_ir(lp: LocalArbProblem) -> Dict: "liquidity_budget": lp.liquidity_budget, "latency_budget": lp.latency_budget, } + + +def map_to_full_ir( + lp: LocalArbProblem, + shared: Optional[SharedSignals] = None, + dual: Optional[DualVariables] = None, + privacy: Optional[PrivacyBudget] = None, + audit: Optional[AuditLog] = None, +) -> Dict: + """Map ArbSphere primitives to a richer, vendor-agnostic IR. + + All arguments are optional to allow incremental construction of the IR as data + becomes available (e.g., after federation steps or governance events). + """ + ir: Dict = map_to_ir(lp) + # Attach optional components if provided + if shared is not None: + ir["shared_signals"] = { + "deltas": list(shared.deltas), + "cross_venue_corr": shared.cross_venue_corr, + "liquidity_availability": dict(shared.liquidity_availability), + "latency_proxy": shared.latency_proxy, + } + if dual is not None: + ir["dual_variables"] = {"shadow_prices": dict(dual.shadow_prices)} + if privacy is not None: + ir["privacy_budget"] = {"budgets": dict(privacy.budgets)} + if audit is not None: + # Flatten audit log entries minimally for the IR; keep structure lightweight + ir["audit_log"] = { + "entries": [ + { + "ts": e.ts, + "event": e.event, + "details": dict(e.details), + "signature": e.signature, + } + for e in audit.entries + ] + } + return ir diff --git a/tests/test_ir_full_mapping.py b/tests/test_ir_full_mapping.py new file mode 100644 index 0000000..f02e2dd --- /dev/null +++ b/tests/test_ir_full_mapping.py @@ -0,0 +1,29 @@ +from arbsphere.primitives import LocalArbProblem, SharedSignals, DualVariables, AuditLog, AuditLogEntry, PrivacyBudget +from arbsphere.ir import map_to_full_ir + + +def test_full_ir_mapping_includes_optional_components(): + lp = LocalArbProblem(asset_pair=("AAPL", "USD"), target_mispricing=0.5, liquidity_budget=100000.0, latency_budget=0.2) + shared = SharedSignals( + deltas=[0.1, -0.05], + cross_venue_corr=0.8, + liquidity_availability={"venue-1": 100000.0, "venue-2": 90000.0}, + latency_proxy=0.6, + ) + dual = DualVariables(shadow_prices={"AAPL/USD": 1.0}) + privacy = PrivacyBudget(budgets={"signals": 0.5}) + audit = AuditLog(entries=[AuditLogEntry(ts=1.0, event="dept", details={"ok": True}, signature="sig")]) + + ir = map_to_full_ir(lp, shared=shared, dual=dual, privacy=privacy, audit=audit) + + # Basic structure checks + assert ir.get("type") == "LocalArbProblem" or ir.get("asset_pair") is not None + assert "asset_pair" in ir and ir["asset_pair"] == ["AAPL", "USD"] + assert "shared_signals" in ir + assert ir["shared_signals"]["cross_venue_corr"] == 0.8 + assert "dual_variables" in ir + assert ir["dual_variables"]["shadow_prices"]["AAPL/USD"] == 1.0 + assert "privacy_budget" in ir + assert ir["privacy_budget"]["budgets"]["signals"] == 0.5 + assert "audit_log" in ir + assert isinstance(ir["audit_log"]["entries"], list)