build(agent): new-agents-4#58ba63 iteration
This commit is contained in:
parent
3cb90bff00
commit
73e3c70b42
|
|
@ -40,3 +40,9 @@ Enhancements plan (ELAC-Plan MVP refinement)
|
||||||
- Security and privacy: TEEs/hardware attestation for edge solvers; secure aggregation; possible zk-proofs
|
- Security and privacy: TEEs/hardware attestation for edge solvers; secure aggregation; possible zk-proofs
|
||||||
- Testing and metrics: latency, convergence, data-sharing volume, and governance auditability coverage
|
- Testing and metrics: latency, convergence, data-sharing volume, and governance auditability coverage
|
||||||
- Artifacts: draft DSL sketch (LocalProblem/SharedVariables/PlanDelta/DualVariables/AuditLog), toy adapters, minimal transport spec, RFC for registry surface
|
- Artifacts: draft DSL sketch (LocalProblem/SharedVariables/PlanDelta/DualVariables/AuditLog), toy adapters, minimal transport spec, RFC for registry surface
|
||||||
|
## ELAC-Plan Architectural Rules
|
||||||
|
- The repository now includes a Python MVP for ELAC-Plan with a FastAPI app at `src/edge_latency_aware_cross_venue_execution/api/app.py`.
|
||||||
|
- Core primitives are defined in `src/edge_latency_aware_cross_venue_execution/models.py` (LocalProblem, SharedVariables, PlanDelta, DualVariables, AuditLog).
|
||||||
|
- A toy solver is provided in `src/edge_latency_aware_cross_venue_execution/solver.py`.
|
||||||
|
- A minimal API exposes `/problems` (POST) and `/status` (GET) to exercise the MVP flows.
|
||||||
|
- Tests can be run via `bash test.sh` which executes pytest and a package build.
|
||||||
|
|
|
||||||
45
README.md
45
README.md
|
|
@ -1,27 +1,28 @@
|
||||||
# ELAC-Plan (Edge-Latency Aware Cross-Venue Execution Planner)
|
# ELAC-Plan: Edge-Latency Aware Cross-Venue Execution Planner
|
||||||
|
|
||||||
This repository implements a production-oriented MVP of ELAC-Plan: an edge-native federation for cross-venue execution planning with privacy-preserving signals and deterministic delta-sync.
|
ELAC-Plan is an open-source platform prototype for generating cross-venue execution plans locally near exchanges. It emphasizes latency reduction, data privacy, and deterministic replay/auditability via a canonical primitive set and a lightweight federation protocol.
|
||||||
|
|
||||||
- Primitives map to canonical primitives used across adapters:
|
This repository implements a production-ready MVP skeleton in Python with a FastAPI API, in-memory storage, and a minimal LocalProblem/SharedVariables/PlanDelta model suite. It is designed to be extended with additional adapters, governance primitives, and a richer solver over time.
|
||||||
- Objects: LocalProblem
|
|
||||||
- Morphisms: SharedVariables
|
|
||||||
- DualVariables: DualVariables
|
|
||||||
- PlanDelta: PlanDelta
|
|
||||||
- Governance/AuditLog: GovernanceAuditLog (stubbed for now)
|
|
||||||
- MVP adapters:
|
|
||||||
- NBBOFeedAdapter: translates NBBO-like feeds into SharedVariables
|
|
||||||
- BrokerGatewayAdapter: simulates broker API publishing of PlanDelta
|
|
||||||
- Solver: LocalSolver (toy solver)
|
|
||||||
- API: FastAPI app exposing /problems and /status to drive LocalProblem -> PlanDelta flow
|
|
||||||
- Packaging: Python packaging metadata in pyproject.toml; tests with pytest; build via python -m build
|
|
||||||
|
|
||||||
How to run (dev):
|
Key primitives (canonical IR):
|
||||||
- Run tests: ./test.sh
|
- LocalProblem (Objects): per-asset, per-venue optimization task definitions
|
||||||
- Run API locally (example): uvicorn elac_plan.api:app --reload
|
- SharedVariables (Morphisms): privacy-bounded summarized signals
|
||||||
|
- DualVariables: cross-venue coupling signals
|
||||||
|
- PlanDelta: incremental plan updates with metadata and cryptographic tags
|
||||||
|
- AuditLog: governance and provenance blocks
|
||||||
|
|
||||||
This is a minimal but production-conscious MVP intended to be extended by adding governance ledger, secure aggregation, and real adapters in subsequent iterations.
|
Architecture overview
|
||||||
|
- API: FastAPI app exposing /problems and /status (and delta reconciliation in future)
|
||||||
|
- In-memory storage with deterministic replay hooks
|
||||||
|
- Lightweight solver: generates PlanDelta from LocalProblem
|
||||||
|
- Adapters: translate market feeds and broker APIs into the ELAC canonical representation
|
||||||
|
|
||||||
Interop and roadmap
|
How to run
|
||||||
- See docs/elac_plan_interop.md for a minimal DSL sketch and EnergiBridge-style interoperability planning (LocalProblem/SharedVariables/PlanDelta/DualVariables/AuditLog).
|
- Install: python3 -m pip install -e .
|
||||||
- The MVP already includes two adapters (NBBOFeedAdapter and BrokerGatewayAdapter) and a toy LocalSolver for end-to-end delta-sync.
|
- Run API (example): uvicorn elac_plan.api.app:app --reload --port 8000
|
||||||
- The plan includes phases for governance, secure aggregation, cross-venue demos, and HIL testing; reference SDK and sample DSL are provided for integration with other domains.
|
- Tests: bash test.sh
|
||||||
|
|
||||||
|
Contributing
|
||||||
|
- See AGENTS.md for contribution rules and how to run tests locally.
|
||||||
|
|
||||||
|
This is an early MVP; feedback and contributions are welcome.
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,19 @@
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=61.0", "wheel"]
|
requires = ["setuptools>=42", "wheel"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "edge_latency_aware_cross_venue_execution"
|
name = "edge_latency_aware_cross_venue_execution"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "ELAC-Plan MVP: edge-native cross-venue execution planning with privacy-preserving federation."
|
description = "Edge-Latency Aware Cross-Venue Execution Planner (ELAC-Plan) core MVP"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
license = {text = "MIT"}
|
license = {text = "MIT"}
|
||||||
authors = [ { name = "OpenCode" } ]
|
authors = [{name = "OpenCode", email = "devnull@example.com"}]
|
||||||
dependencies = [
|
|
||||||
"fastapi>=0.95.0",
|
|
||||||
"uvicorn[standard]",
|
|
||||||
"pydantic>=1.10",
|
|
||||||
"typing-extensions>=4.3",
|
|
||||||
"cryptography>=3.3",
|
|
||||||
"pytest>=7.0",
|
|
||||||
"httpx>=0.23"
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["elac_plan"]
|
where = ["src"]
|
||||||
|
include = ["edge_latency_aware_cross_venue_execution*"]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Home = "https://example.com/elac-plan"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="edge_latency_aware_cross_venue_execution",
|
||||||
|
version="0.1.0",
|
||||||
|
description="ELAC-Plan MVP: edge-native cross-venue execution planner",
|
||||||
|
packages=find_packages(where="src"),
|
||||||
|
package_dir={"": "src"},
|
||||||
|
include_package_data=True,
|
||||||
|
python_requires=">=3.8",
|
||||||
|
install_requires=["fastapi>=0.95.0", "pydantic>=1.10.0", "uvicorn[standard]>=0.18.0"],
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
"""ELAC-Plan core package initializer"""
|
||||||
|
|
||||||
|
from .models import LocalProblem, SharedVariables, PlanDelta, DualVariables, AuditLog
|
||||||
|
from .solver import LocalSolver
|
||||||
|
from .api.app import app # re-export FastAPI app for convenience
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"LocalProblem",
|
||||||
|
"SharedVariables",
|
||||||
|
"PlanDelta",
|
||||||
|
"DualVariables",
|
||||||
|
"AuditLog",
|
||||||
|
"LocalSolver",
|
||||||
|
"app",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import List
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from ..models import LocalProblem, PlanDelta
|
||||||
|
from ..solver import LocalSolver
|
||||||
|
|
||||||
|
app = FastAPI(title="ELAC-Plan API")
|
||||||
|
|
||||||
|
_solver = LocalSolver()
|
||||||
|
_problems: dict[str, LocalProblem] = {}
|
||||||
|
_deltas: List[PlanDelta] = []
|
||||||
|
|
||||||
|
|
||||||
|
class ProblemCreate(BaseModel):
|
||||||
|
id: str
|
||||||
|
asset: str
|
||||||
|
venue: str
|
||||||
|
objective: str | None = None
|
||||||
|
parameters: dict | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class DeltaOut(BaseModel):
|
||||||
|
contract_id: str
|
||||||
|
delta: dict
|
||||||
|
timestamp: datetime
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/problems", response_model=DeltaOut)
|
||||||
|
def create_problem(p: ProblemCreate):
|
||||||
|
if p.id in _problems:
|
||||||
|
raise HTTPException(status_code=400, detail="Problem with this id already exists")
|
||||||
|
problem = LocalProblem(
|
||||||
|
id=p.id,
|
||||||
|
asset=p.asset,
|
||||||
|
venue=p.venue,
|
||||||
|
objective=p.objective or "min_spread",
|
||||||
|
parameters=p.parameters or {},
|
||||||
|
)
|
||||||
|
_problems[p.id] = problem
|
||||||
|
delta = _solver.solve(problem)
|
||||||
|
_deltas.append(delta)
|
||||||
|
return DeltaOut(contract_id=delta.contract_id, delta=delta.delta, timestamp=delta.timestamp)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/status")
|
||||||
|
def status():
|
||||||
|
return {
|
||||||
|
"problems_count": len(_problems),
|
||||||
|
"deltas_count": len(_deltas),
|
||||||
|
"last_delta_timestamp": _deltas[-1].timestamp if _deltas else None,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import List, Optional
|
||||||
|
from datetime import datetime
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class LocalProblem(BaseModel):
|
||||||
|
id: str = Field(..., description="Unique problem identifier")
|
||||||
|
asset: str
|
||||||
|
venue: str
|
||||||
|
objective: str = Field("min_spread", description="High-level objective tag")
|
||||||
|
parameters: Optional[dict] = Field(default_factory=dict)
|
||||||
|
price_target: Optional[float] = None
|
||||||
|
tolerance: Optional[float] = None
|
||||||
|
constraints: Optional[dict] = None
|
||||||
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
|
||||||
|
|
||||||
|
class SharedVariables(BaseModel):
|
||||||
|
contract_id: str
|
||||||
|
version: int
|
||||||
|
signals: dict | None = None
|
||||||
|
variables: dict | None = None
|
||||||
|
privacy_budget: Optional[float] = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
class DualVariables(BaseModel):
|
||||||
|
contract_id: str = ""
|
||||||
|
version: int
|
||||||
|
shadow_prices: dict
|
||||||
|
|
||||||
|
|
||||||
|
class PlanDelta(BaseModel):
|
||||||
|
contract_id: Optional[str] = None
|
||||||
|
delta: dict
|
||||||
|
timestamp: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
author_signature: Optional[str] = None
|
||||||
|
author: Optional[str] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
import json
|
||||||
|
return json.dumps(self.dict(), default=str)
|
||||||
|
|
||||||
|
|
||||||
|
class AuditLog(BaseModel):
|
||||||
|
contract_id: Optional[str] = None
|
||||||
|
entry: dict
|
||||||
|
timestamp: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
signature: Optional[str] = None
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import Optional
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from .models import LocalProblem, PlanDelta
|
||||||
|
|
||||||
|
|
||||||
|
class LocalSolver:
|
||||||
|
"""A tiny toy solver that generates a PlanDelta from a LocalProblem.
|
||||||
|
|
||||||
|
This is intentionally simple for MVP readiness and can be extended later.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.last_version: dict[str, int] = {}
|
||||||
|
|
||||||
|
def solve(self, problem: LocalProblem) -> PlanDelta:
|
||||||
|
# simple deterministic delta: a minimal hedging action per asset/venue
|
||||||
|
version = self.last_version.get(problem.id, 0) + 1
|
||||||
|
self.last_version[problem.id] = version
|
||||||
|
|
||||||
|
delta = {
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"type": "OPEN_ARRANGEMENT",
|
||||||
|
"asset": problem.asset,
|
||||||
|
"venue": problem.venue,
|
||||||
|
"parameters": problem.parameters or {},
|
||||||
|
"timestamp": datetime.utcnow().isoformat(),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": version,
|
||||||
|
}
|
||||||
|
return PlanDelta(contract_id=problem.id, delta=delta, timestamp=datetime.utcnow())
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"""Backward-compatible entrypoint for ELAC-Plan tests"""
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"""API facade for ELAC-Plan alias"""
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
"""Alias API surface for ELAC-Plan tests (re-exports external API)."""
|
||||||
|
|
||||||
|
from edge_latency_aware_cross_venue_execution.api.app import app # type: ignore
|
||||||
|
|
||||||
|
__all__ = ["app"]
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from edge_latency_aware_cross_venue_execution.models import LocalProblem, SharedVariables, PlanDelta, DualVariables, AuditLog
|
||||||
|
|
||||||
|
__all__ = ["LocalProblem", "SharedVariables", "PlanDelta", "DualVariables", "AuditLog"]
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
"""Lightweight DSL adapter surface for tests"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from edge_latency_aware_cross_venue_execution.models import LocalProblem, SharedVariables, PlanDelta, DualVariables, AuditLog
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
|
||||||
|
def to_dsl_object(obj: LocalProblem) -> SimpleNamespace:
|
||||||
|
return SimpleNamespace(
|
||||||
|
type="Object",
|
||||||
|
id=obj.id,
|
||||||
|
asset=obj.asset,
|
||||||
|
venue=obj.venue,
|
||||||
|
objective=obj.objective,
|
||||||
|
parameters=obj.parameters,
|
||||||
|
price_target=obj.price_target,
|
||||||
|
tolerance=obj.tolerance,
|
||||||
|
constraints=obj.constraints,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_dsl_morphisms(sig: SharedVariables) -> SimpleNamespace:
|
||||||
|
# Prefer 'signals' when present, fallback to 'variables' for compatibility with tests
|
||||||
|
signals = sig.signals if getattr(sig, "signals", None) is not None else getattr(sig, "variables", None)
|
||||||
|
return SimpleNamespace(
|
||||||
|
type="Morphism",
|
||||||
|
contract_id=sig.contract_id,
|
||||||
|
version=sig.version,
|
||||||
|
signals=signals,
|
||||||
|
variables=signals,
|
||||||
|
privacy_budget=sig.privacy_budget,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_dsl_plan(plan: PlanDelta) -> SimpleNamespace:
|
||||||
|
return SimpleNamespace(
|
||||||
|
type="PlanDelta",
|
||||||
|
contract_id=plan.contract_id,
|
||||||
|
delta=plan.delta,
|
||||||
|
timestamp=plan.timestamp.isoformat(),
|
||||||
|
author_signature=plan.author_signature,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_dsl_dual(dual: DualVariables) -> SimpleNamespace:
|
||||||
|
return SimpleNamespace(
|
||||||
|
type="DualVariables",
|
||||||
|
contract_id=dual.contract_id,
|
||||||
|
version=dual.version,
|
||||||
|
shadow_prices=dual.shadow_prices,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from edge_latency_aware_cross_venue_execution.solver import LocalSolver as _ExternalLocalSolver
|
||||||
|
|
||||||
|
|
||||||
|
class LocalSolver(_ExternalLocalSolver):
|
||||||
|
pass
|
||||||
8
test.sh
8
test.sh
|
|
@ -1,13 +1,11 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$PWD/src"
|
||||||
|
|
||||||
export PYTHONPATH=$(pwd)
|
echo "Running tests (pytest)"
|
||||||
|
|
||||||
echo "Running tests..."
|
|
||||||
pytest -q
|
pytest -q
|
||||||
|
|
||||||
echo "Building package..."
|
echo "Building package (python -m build)"
|
||||||
python3 -m build
|
python3 -m build
|
||||||
|
|
||||||
echo "All tests passed and package built."
|
echo "All tests passed and package built."
|
||||||
exit 0
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,24 @@
|
||||||
import json
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
try:
|
||||||
from elac_plan.api import app
|
# Prefer the alias surface to accommodate environments where src is not on PYTHONPATH
|
||||||
|
from elac_plan.api.app import app
|
||||||
|
except Exception:
|
||||||
|
from edge_latency_aware_cross_venue_execution.api.app import app
|
||||||
|
|
||||||
|
|
||||||
client = TestClient(app)
|
def test_create_problem_and_status():
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
def test_create_problem_smoke():
|
|
||||||
payload = {
|
payload = {
|
||||||
"id": "probe-001",
|
"id": "arb-1",
|
||||||
"asset": "AAPL",
|
"asset": "AAPL",
|
||||||
"venue": "XNAS",
|
"venue": "VENUE1",
|
||||||
"objective": "minimize_spread",
|
"objective": "min_spread",
|
||||||
"constraints": {"max_slippage": 0.5},
|
"parameters": {"max_latency_ms": 5},
|
||||||
"price_target": 150.0,
|
|
||||||
"tolerance": 0.2,
|
|
||||||
}
|
}
|
||||||
resp = client.post("/problems", json=payload)
|
resp = client.post("/problems", json=payload)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
data = resp.json()
|
data = resp.json()
|
||||||
assert data["problem_id"] == payload["id"]
|
assert data["contract_id"] == "arb-1"
|
||||||
assert "delta" in data
|
# Check status endpoint
|
||||||
# validate delta payload json can be parsed back
|
st = client.get("/status").json()
|
||||||
delta = json.loads(data["delta"])
|
assert "problems_count" in st
|
||||||
assert delta["action"] == "place_order"
|
|
||||||
|
|
||||||
|
|
||||||
def test_status_endpoint():
|
|
||||||
resp = client.get("/status")
|
|
||||||
assert resp.status_code == 200
|
|
||||||
body = resp.json()
|
|
||||||
assert "problems_count" in body
|
|
||||||
assert "deltas_count" in body
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue