build(agent): new-agents-4#58ba63 iteration

This commit is contained in:
agent-58ba63c88b4c9625 2026-04-20 16:17:43 +02:00
parent 02de4d9f97
commit 10a5c36315
6 changed files with 101 additions and 68 deletions

View File

@ -42,3 +42,10 @@ Architecture overview for the EquiCompiler MVP
Notes Notes
- This document is intentionally concise to enable rapid iteration. It can be expanded as the project evolves. - This document is intentionally concise to enable rapid iteration. It can be expanded as the project evolves.
Architectural Additions (MVP scaffolds)
- Graph-of-Contracts (GoC) skeleton: A lightweight registry and skeleton IR GoC to enable plug-and-play adapters (data feeds and brokers) and verifiable contract flows.
- Registry: A tiny in-process GraphRegistry for versioned adapters and contracts.
- Backends: A Python backtester backend scaffold to enable offline backtests and delta-sync workflows.
- Attestations: Lightweight per-step attestations (hash/digest) embedded in the IR for auditability and replay protection.
- GoC/attestation hooks are designed to be gradually fleshed out in subsequent phases without breaking existing MVP behavior.

View File

@ -36,6 +36,11 @@ Development notes
- Ensure tests cover the new functionality and avoid sensitive data in tests. - Ensure tests cover the new functionality and avoid sensitive data in tests.
Next steps Next steps
- Extend the DSL with richer constraints (VaR, VaR-CVaR, liquidity, latency) and ExecutionPolicy primitives.
- Integrate the GoC registry and build a canonical EquiIR representation with per-message metadata for replay/verification.
- Add a lightweight delta-sync coordinator and starter adapters for data feeds and brokers.
- Expand the test suite to exercise the new backtester and Graph-of-Contracts scaffolds.
- Improve packaging and docs to support publishing to a Python package index.
- Implement a more expressive DSL and a richer IR (EquiIR) representation. - Implement a more expressive DSL and a richer IR (EquiIR) representation.
- Add more tests for edge cases and simple integration tests for the CLI. - Add more tests for edge cases and simple integration tests for the CLI.
- Expand packaging metadata and README with a longer developer and user guide. - Expand packaging metadata and README with a longer developer and user guide.

View File

@ -0,0 +1,26 @@
"""Lightweight Python backtester backend scaffold for EquiCompiler MVP."""
from __future__ import annotations
from typing import Dict, Any
def run_backtest(ir: Dict[str, Any], data: Any = None) -> Dict[str, Any]:
"""Deterministic, simple backtest placeholder.
This function is a scaffold. It returns a deterministic, small summary based on
the IR contents (assets and objectives). It is not a real backtester, but it
provides a deterministic bridge for MVP tests and integration with the rest of
the stack.
"""
assets = ir.get("assets", [])
objectives = ir.get("objectives", [])
# Very naive placeholder: return a pretend annualized return depending on asset count
base_return = 0.05 * max(1, len(assets)) if assets else 0.0
summary = {
"assets": assets,
"objective_met": any(o for o in objectives if isinstance(o, str) and o.lower().find("maximize") != -1),
"return": round(base_return, 4),
"trace": [{"step": "init", "value": base_return}],
}
return summary
__all__ = ["run_backtest"]

View File

@ -73,7 +73,36 @@ def _attach_attestations(ir: Dict[str, object]) -> Dict[str, object]:
{"step": "verify", "timestamp": timestamp, "digest": digest, "algorithm": "SHA-256"}, {"step": "verify", "timestamp": timestamp, "digest": digest, "algorithm": "SHA-256"},
] ]
ir_copy["attestations"] = attestations ir_copy["attestations"] = attestations
# Attach a lightweight Graph-of-Contracts (GoC) skeleton to the IR
ir_copy["graph_of_contracts"] = _build_goC_skeleton(ir_copy, digest, timestamp)
return ir_copy return ir_copy
def _build_goC_skeleton(ir_with_attestations: Dict[str, object], base_digest: str, base_timestamp: str) -> Dict[str, object]:
"""Create a minimal Graph-of-Contracts (GoC) skeleton for the IR.
The GoC is a placeholder scaffold intended for early interoperability between
adapters (data feeds, brokers) and the EquiIR. It includes a registry digest and
a generation timestamp to aid replay and auditability.
"""
# Attempt to compute a compact digest from core IR parts to seed the GoC metadata
try:
base_for_goct = {
"assets": ir_with_attestations.get("assets", []),
"objectives": ir_with_attestations.get("objectives", []),
"constraints": ir_with_attestations.get("constraints", {}),
}
digest = hashlib.sha256(json.dumps(base_for_goct, sort_keys=True).encode("utf-8")).hexdigest()
except Exception:
digest = base_digest
return {
"version": "0.1",
"contracts": [],
"metadata": {
"generated_at": base_timestamp,
"source_digest": digest,
},
}
__all__ = ["parse_dsl_to_ir", "to_json"] __all__ = ["parse_dsl_to_ir", "to_json"]

View File

@ -1,79 +1,27 @@
"""Graph of Contracts (GoC) and versioned adapters skeleton. """Graph-of-Contracts (GoC) registry skeleton.
This module provides a tiny in-process registry to model versioned adapters This module provides a tiny, in-process registry to track contracts/adapters
and contracts that would connect the EquiCompiler to data feeds, brokers, that participate in the EquiCompiler GoC workflow. It is intentionally lightweight
and other runtime components. It is intended as a scaffold for future and serves as a scaffold for the MVP; real systems would back this with a durable store.
extensions and to support the MVP where plug-and-play backends are wired in
via stable, versioned contracts.
""" """
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field from typing import Dict, List, Any
from typing import Any, Dict, List, Optional
import time
@dataclass
class VersionedContract:
name: str
version: str
payload: Dict[str, Any] = field(default_factory=dict)
timestamp: float = field(default_factory=lambda: time.time())
def as_dict(self) -> Dict[str, Any]:
return {
"name": self.name,
"version": self.version,
"payload": self.payload,
"timestamp": self.timestamp,
}
class GraphOfContractsRegistry:
"""In-memory registry for versioned contracts/adapters."""
class GraphRegistry:
def __init__(self) -> None: def __init__(self) -> None:
self._contracts: Dict[str, VersionedContract] = {} self._registry: Dict[str, Dict[str, Any]] = {}
self._adapters: Dict[str, VersionedContract] = {}
# Contracts management def register_contract(self, name: str, version: str, adapter: str) -> Dict[str, str]:
def register_contract(self, name: str, version: str, payload: Optional[Dict[str, Any]] = None) -> VersionedContract:
payload = payload or {}
vc = VersionedContract(name=name, version=version, payload=payload)
key = f"{name}:{version}" key = f"{name}:{version}"
self._contracts[key] = vc entry = {"name": name, "version": version, "adapter": adapter}
return vc self._registry[key] = entry
return entry
def get_contract(self, name: str, version: Optional[str] = None) -> Optional[VersionedContract]: def get_contract(self, name: str, version: str) -> Dict[str, Any]:
if version is None: return self._registry.get(f"{name}:{version}", {})
# Return the latest by timestamp if available
candidates = [c for k, c in self._contracts.items() if k.startswith(f"{name}:")]
if not candidates:
return None
return max(candidates, key=lambda c: c.timestamp)
return self._contracts.get(f"{name}:{version}")
def list_contracts(self) -> List[VersionedContract]: def list_contracts(self) -> List[Dict[str, Any]]:
return list(self._contracts.values()) return list(self._registry.values())
# Adapters management (GoC entries) __all__ = ["GraphRegistry"]
def register_adapter(self, adapter_name: str, version: str, payload: Optional[Dict[str, Any]] = None) -> VersionedContract:
payload = payload or {}
ac = VersionedContract(name=adapter_name, version=version, payload=payload)
key = f"adapter:{adapter_name}:{version}"
self._adapters[key] = ac
return ac
def get_adapter(self, adapter_name: str, version: Optional[str] = None) -> Optional[VersionedContract]:
if version is None:
candidates = [a for k, a in self._adapters.items() if k.startswith(f"adapter:{adapter_name}:")]
if not candidates:
return None
return max(candidates, key=lambda c: c.timestamp)
return self._adapters.get(f"adapter:{adapter_name}:{version}")
def list_adapters(self) -> List[VersionedContract]:
return list(self._adapters.values())
__all__ = ["GraphOfContractsRegistry", "VersionedContract"]

View File

@ -16,6 +16,24 @@ class TestCore(unittest.TestCase):
self.assertEqual(ir["constraints"]["max_drawdown"], "0.2") self.assertEqual(ir["constraints"]["max_drawdown"], "0.2")
self.assertEqual(ir["constraints"]["var"], "0.95") self.assertEqual(ir["constraints"]["var"], "0.95")
def test_attestations_present_and_digest_format(self):
dsl = (
"assets: AAPL, MSFT, GOOG\n"
"objectives: maximize_return\n"
"constraints: max_drawdown=0.2, var=0.95"
)
ir = parse_dsl_to_ir(dsl)
# Attestations should be present and include digest fields with hex digest
self.assertIn("attestations", ir)
attestations = ir["attestations"]
self.assertIsInstance(attestations, list)
self.assertGreaterEqual(len(attestations), 1)
import re
for att in attestations:
self.assertIn("digest", att)
self.assertIsInstance(att["digest"], str)
self.assertRegex(att["digest"], r"^[0-9a-f]{64}$")
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()