build(agent): molt-z#db0ec5 iteration
This commit is contained in:
parent
2b3b39e76e
commit
653f014c17
|
|
@ -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,29 @@
|
||||||
|
Agents and Architecture
|
||||||
|
- Language: Python (production-ready, packaging via pyproject.toml)
|
||||||
|
- Core concepts:
|
||||||
|
- LocalProblem: DSL-typed description of assets, objectives, and constraints for a portfolio/strategy task.
|
||||||
|
- PlanDelta: incremental updates to a LocalProblem, with cryptographic tags for provenance.
|
||||||
|
- AuditLog: cryptographic attestations of decisions and transformations.
|
||||||
|
- Signer: simple HMAC-based signer to prove integrity of plan deltas (no external dependencies).
|
||||||
|
- Adapters: pluggable interfaces that translate device-specific data into the canonical IR and perform actions (readState, exposeLocalProblemData, applyCommand).
|
||||||
|
- Graph-of-Contracts registry: lightweight in-process registry tracking adapters and contract versions.
|
||||||
|
- Backtester: deterministic simulator to validate learned strategies against historical data in offline mode.
|
||||||
|
|
||||||
|
Repo structure
|
||||||
|
- marketcompiler_verifiable_dsl_edge_compi/
|
||||||
|
- __init__.py
|
||||||
|
- core.py # DSL data models (LocalProblem, PlanDelta, AuditLog)
|
||||||
|
- signer.py # Sign/verify utilities (HMAC-based)
|
||||||
|
- adapters.py # AbstractAdapter + two starter adapters (PriceFeedAdapter, MockBrokerAdapter)
|
||||||
|
- registry.py # Graph-of-Contracts (registry) and contract/version storage
|
||||||
|
- backtest.py # Simple offline backtester
|
||||||
|
tests/
|
||||||
|
- test_core.py # Core unit tests for serialization, signing, and backtesting
|
||||||
|
- __init__.py
|
||||||
|
|
||||||
|
Development workflow
|
||||||
|
- Run tests with: ./test.sh
|
||||||
|
- Publish planning artifacts once tests pass and READY_TO_PUBLISH is present.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
- This is a skeleton MVP. It intentionally emphasizes portability, auditability, and offline testing while keeping the implementation approachable for extension.
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# MarketCompiler Verifiable DSL Edge Compi — MVP Guide
|
||||||
|
|
||||||
|
This repository provides a minimal, production-oriented MVP of a verifiable DSL edge compiler for investment strategies. It focuses on portability, auditability, and offline-first testing.
|
||||||
|
|
||||||
|
What’s included in this MVP
|
||||||
|
- LocalProblem: DSL for assets, objectives, and constraints.
|
||||||
|
- PlanDelta and AuditLog: compact plan changes with cryptographic attestations.
|
||||||
|
- Signer: HMAC-based signer to prove integrity of plan deltas.
|
||||||
|
- Adapters: AbstractAdapter with two starter adapters (PriceFeedAdapter, MockBrokerAdapter).
|
||||||
|
- GraphOfContracts: tiny registry for adapter schemas and versions.
|
||||||
|
- Backtester: deterministic offline simulator to validate DSL-driven plans.
|
||||||
|
|
||||||
|
How to use
|
||||||
|
- Run tests: ./test.sh
|
||||||
|
- Build: python3 -m build
|
||||||
|
- Explore adapters and core models via the Python package marketcompiler_verifiable_dsl_edge_compi.
|
||||||
|
|
||||||
|
Roadmap (high level)
|
||||||
|
- Phase 0: Core DSL, two starter adapters, local backtester, delta-sync skeleton, and tamper-evident logs.
|
||||||
|
- Phase 1: Add risk budgets (VaR, CVaR) and drawdown constraints; governance ledger; signing of plans.
|
||||||
|
- Phase 2: Cross-exchange adapters and a simple optimizer back-end; edge SDK skeleton.
|
||||||
|
- Phase 3: End-to-end test harness with simulated markets; performance and reproducibility metrics.
|
||||||
|
|
||||||
|
If you want, I can draft more concrete DSL sketches and a toy adapter pair to bootstrap integration.
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
"""MarketCompiler Verifiable DSL Edge Compi (SDK Core)
|
||||||
|
|
||||||
|
Lightweight MVP scaffold for a verifiable DSL to IR compiler with offline testing.
|
||||||
|
This package provides:
|
||||||
|
- core models (LocalProblem, PlanDelta, AuditLog)
|
||||||
|
- a simple HMAC-based Signer
|
||||||
|
- adapters interface and two starter adapters (price feed, mock broker)
|
||||||
|
- a tiny Graph-of-Contracts registry
|
||||||
|
- a basic deterministic backtester
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .core import LocalProblem, PlanDelta, AuditLog
|
||||||
|
from .signer import Signer
|
||||||
|
from .adapters import AbstractAdapter, PriceFeedAdapter, MockBrokerAdapter
|
||||||
|
from .registry import GraphOfContracts
|
||||||
|
from .backtest import Backtester
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"LocalProblem",
|
||||||
|
"PlanDelta",
|
||||||
|
"AuditLog",
|
||||||
|
"Signer",
|
||||||
|
"AbstractAdapter",
|
||||||
|
"PriceFeedAdapter",
|
||||||
|
"MockBrokerAdapter",
|
||||||
|
"GraphOfContracts",
|
||||||
|
"Backtester",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""Starter adapters for MarketCompiler MVP"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from .core import LocalProblem
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractAdapter(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def readState(self) -> Dict[str, Any]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def exposeLocalProblemData(self, lp: LocalProblem) -> Dict[str, Any]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def applyCommand(self, command: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PriceFeedAdapter(AbstractAdapter):
|
||||||
|
"""A tiny in-process price feed adapter with mock data."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._state = {"prices": {"AAPL": 150.0, "MSFT": 300.0}}
|
||||||
|
|
||||||
|
def readState(self) -> Dict[str, Any]:
|
||||||
|
return {"prices": self._state["prices"]}
|
||||||
|
|
||||||
|
def exposeLocalProblemData(self, lp: LocalProblem) -> Dict[str, Any]:
|
||||||
|
return {"lp_id": lp.id, "assets": lp.assets}
|
||||||
|
|
||||||
|
def applyCommand(self, command: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
# No-op for starter adapter
|
||||||
|
return {"status": "ok", "command": command}
|
||||||
|
|
||||||
|
|
||||||
|
class MockBrokerAdapter(AbstractAdapter):
|
||||||
|
"""Mock broker to simulate order placement and fills."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._positions: Dict[str, float] = {}
|
||||||
|
self._logs: list = []
|
||||||
|
|
||||||
|
def readState(self) -> Dict[str, Any]:
|
||||||
|
return {"positions": self._positions}
|
||||||
|
|
||||||
|
def exposeLocalProblemData(self, lp: LocalProblem) -> Dict[str, Any]:
|
||||||
|
return {"lp_id": lp.id, "assets": lp.assets, "constraints": lp.constraints}
|
||||||
|
|
||||||
|
def applyCommand(self, command: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
# Very tiny sim: update positions if command contains orders
|
||||||
|
orders = command.get("orders", {})
|
||||||
|
for asset, qty in orders.items():
|
||||||
|
self._positions[asset] = self._positions.get(asset, 0.0) + float(qty)
|
||||||
|
self._logs.append((asset, qty))
|
||||||
|
return {"status": "filled", "positions": self._positions}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
"""Simple deterministic backtester for MVP"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Dict, List, Any
|
||||||
|
from .core import LocalProblem
|
||||||
|
|
||||||
|
|
||||||
|
class Backtester:
|
||||||
|
@staticmethod
|
||||||
|
def run(lp: LocalProblem, prices: Dict[str, float] | None = None) -> Dict[str, Any]:
|
||||||
|
if prices is None:
|
||||||
|
prices = {a: 100.0 for a in lp.assets}
|
||||||
|
# Very small deterministic scoring: equal-weight returns based on price changes (simulated)
|
||||||
|
total = 0.0
|
||||||
|
for a in lp.assets:
|
||||||
|
p = prices.get(a, 100.0)
|
||||||
|
total += max(0.0, p - 100.0)
|
||||||
|
# Simple metrics
|
||||||
|
result = {
|
||||||
|
"assets": lp.assets,
|
||||||
|
"score": total,
|
||||||
|
"iterations": 1,
|
||||||
|
"timestamp": "2026-01-01T00:00:00Z",
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
"""Core DSL data models for MarketCompiler MVP"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from dataclasses import dataclass, asdict, field
|
||||||
|
from typing import List, Dict, Any
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
def _now_iso() -> str:
|
||||||
|
return datetime.utcnow().isoformat() + "Z"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LocalProblem:
|
||||||
|
id: str
|
||||||
|
assets: List[str]
|
||||||
|
objectives: Dict[str, float]
|
||||||
|
constraints: Dict[str, float]
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self), sort_keys=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(data: str) -> "LocalProblem":
|
||||||
|
d = json.loads(data)
|
||||||
|
return LocalProblem(
|
||||||
|
id=d["id"],
|
||||||
|
assets=d.get("assets", []),
|
||||||
|
objectives=d.get("objectives", {}),
|
||||||
|
constraints=d.get("constraints", {}),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlanDelta:
|
||||||
|
delta: Dict[str, Any]
|
||||||
|
timestamp: str = field(default_factory=_now_iso)
|
||||||
|
author: str = ""
|
||||||
|
contract_id: str = ""
|
||||||
|
version: int = 0
|
||||||
|
privacy_budget: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self), sort_keys=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(data: str) -> "PlanDelta":
|
||||||
|
d = json.loads(data)
|
||||||
|
return PlanDelta(
|
||||||
|
delta=d["delta"],
|
||||||
|
timestamp=d.get("timestamp", _now_iso()),
|
||||||
|
author=d.get("author", ""),
|
||||||
|
contract_id=d.get("contract_id", ""),
|
||||||
|
version=d.get("version", 0),
|
||||||
|
privacy_budget=d.get("privacy_budget", {}),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AuditLog:
|
||||||
|
entry: str
|
||||||
|
signer: str
|
||||||
|
timestamp: str = field(default_factory=_now_iso)
|
||||||
|
contract_id: str = ""
|
||||||
|
version: int = 0
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self), sort_keys=True)
|
||||||
|
@staticmethod
|
||||||
|
def from_json(data: str) -> "AuditLog":
|
||||||
|
d = json.loads(data)
|
||||||
|
return AuditLog(
|
||||||
|
entry=d["entry"], signer=d["signer"], timestamp=d.get("timestamp", _now_iso()), contract_id=d.get("contract_id", ""), version=d.get("version", 0)
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
"""Graph-of-Contracts registry (lightweight)"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
_registry: Dict[str, Dict[str, Any]] = {}
|
||||||
|
|
||||||
|
|
||||||
|
class GraphOfContracts:
|
||||||
|
@staticmethod
|
||||||
|
def register(contract_id: str, schema: Dict[str, Any]) -> None:
|
||||||
|
_registry[contract_id] = schema
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_schema(contract_id: str) -> Dict[str, Any]:
|
||||||
|
return _registry.get(contract_id, {})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def all_contracts() -> Dict[str, Dict[str, Any]]:
|
||||||
|
return dict(_registry)
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
"""Simple HMAC-based signer for PlanDelta and AuditLog proofs."""
|
||||||
|
import os
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
def _key() -> bytes:
|
||||||
|
# Use environment-provided key for reproducibility in tests; fall back to a default (not secure!) key.
|
||||||
|
key = os.environ.get("MARKETCOMPILER_SIGNING_KEY", "default-secret-key").encode("utf-8")
|
||||||
|
return key
|
||||||
|
|
||||||
|
|
||||||
|
class Signer:
|
||||||
|
@staticmethod
|
||||||
|
def sign(message: str) -> str:
|
||||||
|
key = _key()
|
||||||
|
digest = hmac.new(key, message.encode("utf-8"), hashlib.sha256).hexdigest()
|
||||||
|
return digest
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def verify(message: str, signature: str) -> bool:
|
||||||
|
expected = Signer.sign(message)
|
||||||
|
return hmac.compare_digest(expected, signature)
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "marketcompiler_verifiable_dsl_edge_compi"
|
||||||
|
version = "0.0.1"
|
||||||
|
description = "Verifiable DSL edge compiler for investment strategies (MVP)"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
license = { file = "LICENSE" }
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["."]
|
||||||
|
|
||||||
|
[tool.setuptools.dynamic]
|
||||||
|
version = { attr = "__version__" }
|
||||||
|
|
||||||
|
# Removed deprecated sdist configuration to align with setuptools Kingfisher's policy
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
echo "Running tests with pytest..."
|
||||||
|
pytest -q
|
||||||
|
echo "Building package..."
|
||||||
|
python3 -m build --wheel --no-isolation
|
||||||
|
echo "All tests and build succeeded."
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pathlib
|
||||||
|
# Ensure the repository root is on sys.path for pytest imports
|
||||||
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
if str(ROOT) not in sys.path:
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
|
||||||
|
from marketcompiler_verifiable_dsl_edge_compi.core import LocalProblem, PlanDelta, AuditLog
|
||||||
|
from marketcompiler_verifiable_dsl_edge_compi.signer import Signer
|
||||||
|
from marketcompiler_verifiable_dsl_edge_compi.backtest import Backtester
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_problem_serialization():
|
||||||
|
lp = LocalProblem(id="lp-001", assets=["AAPL", "MSFT"], objectives={"return": 0.1}, constraints={"max_drawdown": 0.2})
|
||||||
|
s = lp.to_json()
|
||||||
|
lp2 = LocalProblem.from_json(s)
|
||||||
|
assert lp2.id == lp.id
|
||||||
|
assert lp2.assets == lp.assets
|
||||||
|
|
||||||
|
|
||||||
|
def test_plan_delta_signing_and_json_roundtrip():
|
||||||
|
delta = PlanDelta(delta={"step": "init"}, author="tester", contract_id="c1", version=1)
|
||||||
|
payload = delta.to_json()
|
||||||
|
sig = Signer.sign(payload)
|
||||||
|
assert Signer.verify(payload, sig)
|
||||||
|
|
||||||
|
# Roundtrip through JSON keeps signature validity conceptually
|
||||||
|
delta2 = PlanDelta.from_json(payload)
|
||||||
|
assert delta2.version == delta.version
|
||||||
|
|
||||||
|
|
||||||
|
def test_backtester_runs():
|
||||||
|
lp = LocalProblem(id="lp-002", assets=["AAPL", "MSFT"], objectives={"return": 0.08}, constraints={"liquidity": 0.5})
|
||||||
|
res = Backtester.run(lp)
|
||||||
|
assert isinstance(res, dict)
|
||||||
|
assert "score" in res
|
||||||
Loading…
Reference in New Issue