build(agent): new-agents#a6e6ec iteration
This commit is contained in:
parent
79145d3ce1
commit
532a632010
|
|
@ -0,0 +1,21 @@
|
||||||
|
node_modules/
|
||||||
|
.npmrc
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
__tests__/
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.cache/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
tmp/
|
||||||
|
.tmp/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
*.egg-info/
|
||||||
|
.pytest_cache/
|
||||||
|
READY_TO_PUBLISH
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
Architecture and contribution rules for ExoRoute MVP (Deterministic Replay).
|
||||||
|
|
||||||
|
Overview
|
||||||
|
- ExoRoute is a cross-venue order routing orchestrator prototype with deterministic replay capabilities.
|
||||||
|
- The design emphasizes privacy by design, governance provenance, and modular adapters.
|
||||||
|
|
||||||
|
Tech Stack (current):
|
||||||
|
- Language: Python 3.9+ (production-ready scaffolding)
|
||||||
|
- Core concepts: LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog, GraphOfContractsRegistry
|
||||||
|
- Adapters: exoroute.adapters.* (starter adapters: FIX/WebSocket feed, simulated venue)
|
||||||
|
- Canonical bridge: exoroute.energi_bridge ( EnergiBridge-style interoperability layer)
|
||||||
|
- Planning: exoroute.planner (naive ADMM-lite placeholder for MVP)
|
||||||
|
- Delta storage: exoroute.delta_store (filesystem-based log of deltas)
|
||||||
|
- API: exoroute.api (FastAPI endpoints) and exoroute.server (runner)
|
||||||
|
|
||||||
|
Contribution Rules
|
||||||
|
- Follow the coding style in current repo (Python with type hints, dataclasses).
|
||||||
|
- Add unit tests where feasible; ensure test.sh passes.
|
||||||
|
- Update AGENTS.md if architecture or testing requirements change.
|
||||||
|
|
||||||
|
Testing and Local Run
|
||||||
|
- To run API locally: python -m exoroute.server # via entrypoint or uvicorn
|
||||||
|
- Use test.sh (in repo root) to validate packaging and basic checks (to be added in patch).
|
||||||
|
|
||||||
|
Decision Log
|
||||||
|
- This document should be kept up to date with architectural shifts and interface changes.
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
from .core import LocalProblem, SharedVariables, PlanDelta, DualVariables, PrivacyBudget, AuditLog
|
||||||
|
__all__ = [
|
||||||
|
"LocalProblem",
|
||||||
|
"SharedVariables",
|
||||||
|
"PlanDelta",
|
||||||
|
"DualVariables",
|
||||||
|
"PrivacyBudget",
|
||||||
|
"AuditLog",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"""Adapters package for ExoRoute: starter adapters for price feeds and simulated venues."""
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
import time
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
class FIXWSFeedAdapter:
|
||||||
|
"""Minimal FIX/WebSocket feed adapter scaffold.
|
||||||
|
Produces deterministic synthetic price signals for testing.
|
||||||
|
"""
|
||||||
|
def __init__(self, endpoint: str = "wss://dummy-feed.example", symbol: str = "ABC"):
|
||||||
|
self.endpoint = endpoint
|
||||||
|
self.symbol = symbol
|
||||||
|
self._start = time.time()
|
||||||
|
|
||||||
|
def get_latest_signal(self) -> Dict[str, Any]:
|
||||||
|
t = time.time() - self._start
|
||||||
|
# Deterministic pseudo-price signal based on time
|
||||||
|
base = 100.0
|
||||||
|
price = base + (t * 0.5) + ((hash(self.symbol) & 0xFFFF) * 0.0001)
|
||||||
|
signal = {
|
||||||
|
"source": "FIXWS",
|
||||||
|
"symbol": self.symbol,
|
||||||
|
"price": round(price, 4),
|
||||||
|
"ts": time.time(),
|
||||||
|
}
|
||||||
|
return signal
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
import time
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
class SimulatedVenueAdapter:
|
||||||
|
"""A simple simulated venue adapter that emits synthetic data."""
|
||||||
|
def __init__(self, venue_id: str = "venue-1"):
|
||||||
|
self.venue_id = venue_id
|
||||||
|
self._start = time.time()
|
||||||
|
|
||||||
|
def get_latest_signal(self) -> Dict[str, Any]:
|
||||||
|
t = time.time() - self._start
|
||||||
|
price = 50.0 + (t * 0.3) + ((hash(self.venue_id) & 0xFFFF) * 0.00007)
|
||||||
|
return {
|
||||||
|
"venue": self.venue_id,
|
||||||
|
"price": round(price, 4),
|
||||||
|
"ts": time.time(),
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
import time
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from .delta_store import DeltaStore
|
||||||
|
from .core import LocalProblem, SharedVariables, PlanDelta
|
||||||
|
from .planner import naive_admm_lite_plan
|
||||||
|
from .energi_bridge import EnergiBridge
|
||||||
|
|
||||||
|
app = FastAPI(title="ExoRoute MVP API")
|
||||||
|
|
||||||
|
delta_store = DeltaStore()
|
||||||
|
|
||||||
|
|
||||||
|
class LocalProblemInput(BaseModel):
|
||||||
|
id: str
|
||||||
|
domain: str
|
||||||
|
assets: list[str]
|
||||||
|
objective: dict[str, object]
|
||||||
|
constraints: dict[str, object]
|
||||||
|
|
||||||
|
|
||||||
|
class SignalPayload(BaseModel):
|
||||||
|
venue: str
|
||||||
|
price: float
|
||||||
|
ts: float
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def read_root():
|
||||||
|
return {"status": "ok", "time": time.time()}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/signal")
|
||||||
|
def submit_signal(payload: SignalPayload):
|
||||||
|
# For MVP, store signals in delta_store as part of a delta
|
||||||
|
delta = PlanDelta(delta={"venue": payload.venue, "price": payload.price, "ts": payload.ts})
|
||||||
|
delta_store.append_delta(delta)
|
||||||
|
return {"stored": True}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/local-problem")
|
||||||
|
def create_local_problem(lp: LocalProblemInput):
|
||||||
|
lp_obj = LocalProblem(
|
||||||
|
id=lp.id,
|
||||||
|
domain=lp.domain,
|
||||||
|
assets=lp.assets,
|
||||||
|
objective=lp.objective,
|
||||||
|
constraints=lp.constraints,
|
||||||
|
)
|
||||||
|
# Create a minimal shared variable placeholder
|
||||||
|
shared = SharedVariables(forecasts={}, priors={}, version=1, timestamp=time.time())
|
||||||
|
delta = naive_admm_lite_plan({lp.id: {"price": 0.0}}, lp_obj, shared)
|
||||||
|
delta_store.append_delta(delta)
|
||||||
|
return delta.delta
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/plan")
|
||||||
|
def get_latest_plan():
|
||||||
|
deltas = delta_store.read_deltas()
|
||||||
|
return {"deltas": deltas[-5:]} # last few for visibility
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Dict, List, Any
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LocalProblem:
|
||||||
|
id: str
|
||||||
|
domain: str
|
||||||
|
assets: List[str]
|
||||||
|
objective: Dict[str, Any]
|
||||||
|
constraints: Dict[str, Any]
|
||||||
|
solver_hint: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SharedVariables:
|
||||||
|
forecasts: Dict[str, Any] # per-venue forecasts
|
||||||
|
priors: Dict[str, Any] # prior solutions or priors
|
||||||
|
version: int = 0
|
||||||
|
timestamp: float = field(default_factory=lambda: time.time())
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlanDelta:
|
||||||
|
delta: Dict[str, Any]
|
||||||
|
timestamp: float = field(default_factory=lambda: time.time())
|
||||||
|
author: str = "system"
|
||||||
|
contract_id: str = "default"
|
||||||
|
signature: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DualVariables:
|
||||||
|
multipliers: Dict[str, Any] # Lagrange multipliers or dual vars
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PrivacyBudget:
|
||||||
|
signal: str
|
||||||
|
budget: float
|
||||||
|
expiry: float
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AuditLog:
|
||||||
|
entry: str
|
||||||
|
signer: str
|
||||||
|
timestamp: float
|
||||||
|
contract_id: str
|
||||||
|
version: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GraphOfContractsRegistryEntry:
|
||||||
|
adapter_id: str
|
||||||
|
supported_domains: List[str]
|
||||||
|
contract_version: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GraphOfContractsRegistry:
|
||||||
|
entries: Dict[str, GraphOfContractsRegistryEntry] = field(default_factory=dict)
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Dict, Any
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from .core import PlanDelta
|
||||||
|
|
||||||
|
|
||||||
|
class DeltaStore:
|
||||||
|
def __init__(self, path: str = ".exoroute_deltas"):
|
||||||
|
self.path = Path(path)
|
||||||
|
self.path.mkdir(parents=True, exist_ok=True)
|
||||||
|
self.log_file = self.path / "deltas.log"
|
||||||
|
if not self.log_file.exists():
|
||||||
|
self.log_file.write_text("")
|
||||||
|
|
||||||
|
def append_delta(self, delta: PlanDelta) -> None:
|
||||||
|
entry = {
|
||||||
|
"delta": delta.delta,
|
||||||
|
"timestamp": delta.timestamp,
|
||||||
|
"author": delta.author,
|
||||||
|
"contract_id": delta.contract_id,
|
||||||
|
"signature": delta.signature,
|
||||||
|
}
|
||||||
|
with self.log_file.open("a", encoding="utf-8") as f:
|
||||||
|
f.write(json.dumps(entry, sort_keys=True) + "\n")
|
||||||
|
|
||||||
|
def read_deltas(self) -> List[Dict[str, Any]]:
|
||||||
|
if not self.log_file.exists():
|
||||||
|
return []
|
||||||
|
deltas = []
|
||||||
|
with self.log_file.open("r", encoding="utf-8") as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
deltas.append(json.loads(line))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
return deltas
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
self.log_file.write_text("")
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import Dict, Any, Optional
|
||||||
|
from .core import LocalProblem, SharedVariables, PlanDelta
|
||||||
|
|
||||||
|
class EnergiBridge:
|
||||||
|
"""Minimal bridge translating ExoRoute primitives to a canonical IR (EnergiBridge-style).
|
||||||
|
This is a lightweight, extensible mapping scaffold for interoperability with CatOpt-like ecosystems.
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def to_canonical(local: LocalProblem, shared: SharedVariables, delta: Optional[PlanDelta] = None) -> Dict[str, Any]:
|
||||||
|
can = {
|
||||||
|
"Objects": {
|
||||||
|
"LocalProblem": {
|
||||||
|
"id": local.id,
|
||||||
|
"domain": local.domain,
|
||||||
|
"assets": local.assets,
|
||||||
|
"objective": local.objective,
|
||||||
|
"constraints": local.constraints,
|
||||||
|
"solver_hint": local.solver_hint,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Morphisms": {
|
||||||
|
"SharedVariables": {
|
||||||
|
"forecasts": getattr(shared, "forecasts", {}),
|
||||||
|
"priors": getattr(shared, "priors", {}),
|
||||||
|
"version": getattr(shared, "version", 0),
|
||||||
|
"timestamp": getattr(shared, "timestamp", 0.0),
|
||||||
|
},
|
||||||
|
"DualVariables": {},
|
||||||
|
},
|
||||||
|
"PlanDelta": delta.delta if delta else {},
|
||||||
|
}
|
||||||
|
return can
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import Dict, Any
|
||||||
|
from .core import LocalProblem, SharedVariables, PlanDelta
|
||||||
|
|
||||||
|
|
||||||
|
def naive_admm_lite_plan(per_venue_signals: Dict[str, Any], local: LocalProblem, shared: SharedVariables) -> PlanDelta:
|
||||||
|
"""A tiny, deterministic placeholder ADMM-lite planner.
|
||||||
|
It computes a simple routing/hedging delta based on signals and the local objective.
|
||||||
|
This is NOT a production-grade optimizer; it's a seed for MVP wiring.
|
||||||
|
"""
|
||||||
|
# Build a toy delta that encodes the sum of signals and a rough budget heuristic
|
||||||
|
total_signal = 0.0
|
||||||
|
for v, s in per_venue_signals.items():
|
||||||
|
price = s.get("price", 0.0)
|
||||||
|
total_signal += price
|
||||||
|
|
||||||
|
delta = {
|
||||||
|
"type": "demo-plan",
|
||||||
|
"local_problem_id": local.id,
|
||||||
|
"objective_summary": local.objective,
|
||||||
|
"total_signal": total_signal,
|
||||||
|
"venues_considered": list(per_venue_signals.keys()),
|
||||||
|
"timestamp": __import__('time').time(),
|
||||||
|
}
|
||||||
|
return PlanDelta(delta=delta, author="planner", contract_id=local.id)
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import uvicorn
|
||||||
|
from .api import app
|
||||||
|
|
||||||
|
def run(host: str = "0.0.0.0", port: int = 8000):
|
||||||
|
uvicorn.run(app, host=host, port=port)
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "exoroute"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Cross-venue order routing orchestrator prototype with deterministic replay"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
license = {text = "MIT"}
|
||||||
|
authors = [{name = "OpenCode"}]
|
||||||
|
dependencies = [
|
||||||
|
"fastapi>=0.105.0",
|
||||||
|
"uvicorn[standard]>=0.22.0",
|
||||||
|
"pydantic>=1.10,<2",
|
||||||
|
"cryptography>=38.0.0",
|
||||||
|
"typing-extensions>=4.0",
|
||||||
|
"requests>=2.28.0"
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["exoroute"]
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo "[TEST] Building package..."
|
||||||
|
python -m build --wheel --no-isolation
|
||||||
|
|
||||||
|
echo "[TEST] Installing in editable mode..."
|
||||||
|
python -m pip install -e .
|
||||||
|
|
||||||
|
echo "[TEST] Quick import sanity..."
|
||||||
|
python - << 'PY'
|
||||||
|
import exoroute.api as api
|
||||||
|
print('API import OK', api.__name__)
|
||||||
|
PY
|
||||||
|
|
||||||
|
echo "[TEST] All basic tests passed."
|
||||||
Loading…
Reference in New Issue