diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd5590b --- /dev/null +++ b/.gitignore @@ -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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..bbc8db0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,32 @@ +DeltaX-Forge SWARM Agent Guide + +Overview +- DeltaX-Forge is a cross-venue delta-driven execution planner with a canonical DSL and a two-tier solver architecture. +- Core stack uses Python for production-grade components: a lightweight DSL, local venue solvers, a central curator, adapters for data feeds and execution, and a governance/logging layer. + +Project Architecture (high level) +- deltax_forge_cross/ + - __init__.py + - dsl.py # Canonical StrategyDelta DSL (Assets, Legs, MarketSignal, PlanDelta, AuditLog) (see tests for usage) +- deltax_forge_cross/core/ + - local_venue_solver.py # Local solver implementation + - central_curator.py # Central coordination/consensus surrogate +- deltax_forge_cross/adapters/ + - price_feed_adapter.py # Translates MarketSignal to a feed dict + - venue_execution_adapter.py # Translates PlanDelta to an execution map +- deltax_forge_cross/logging.py # Deterministic replay logging +- deltax_forge_cross/ledger.py # Lightweight cryptographic signing (HMAC-based) +- tests/ # pytest suite (tests/test_deltax.py) + +Testing and CI +- Test command: bash test.sh +- test.sh builds, installs, and runs pytest +- The tests exercise DSL, local/central solvers, adapters, and governance signing + +How to contribute +- Run tests locally with: bash test.sh +- Ensure new features have unit tests; keep changes minimal and well-scoped +- Maintain backward compatibility unless explicitly required by a breaking change + +Notes +- The repository is intentionally self-contained; it provides a minimal MVP for the DeltaX-Forge concept. diff --git a/README.md b/README.md index 8c12d41..b6a8ed1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ -# idea149-deltax-forge-cross +# DeltaX-Forge: Cross-Venue Delta-Driven Execution Slicer (MVP) -Source logic for Idea #149 \ No newline at end of file +DeltaX-Forge provides a minimal, production-oriented blueprint for cross-venue hedging using a canonical StrategyDelta DSL, a two-tier solver, and deterministic governance logging. + +- Core DSL: Assets, Legs, MarketSignal, PlanDelta, and AuditLogEntry. +- LocalVenueSolver: lightweight per-venue delta computation. +- CentralCurator: cross-venue coherence and coordination. +- Adapters: price feed and venue execution translations. +- Deterministic replay and governance ledger via cryptographic signing. + +How to run tests locally +- Ensure Python 3.11+ is available. +- Run: bash test.sh +- This will build, install, and run the test suite. + +Packaging notes +- This repository is designed to be installed from a wheel or sdist. The test script installs the produced wheel before running tests to ensure import paths are resolvable in CI environments. + +Hooked commands +- See AGENTS.md for developer-oriented information and testing commands. diff --git a/deltax_forge_cross/__init__.py b/deltax_forge_cross/__init__.py new file mode 100644 index 0000000..ed869f2 --- /dev/null +++ b/deltax_forge_cross/__init__.py @@ -0,0 +1,23 @@ +from .dsl import Asset, Leg, MarketSignal, StrategyDelta, PlanDelta, AuditLogEntry +from .core.local_venue_solver import LocalVenueSolver +from .core.central_curator import CentralCurator +from .adapters.price_feed_adapter import translate_market_signals +from .adapters.venue_execution_adapter import translate_plan_delta +from .logging import RunLog +from .ledger import LedgerSigner, sign_and_append + +__all__ = [ + "Asset", + "Leg", + "MarketSignal", + "StrategyDelta", + "PlanDelta", + "AuditLogEntry", + "LocalVenueSolver", + "CentralCurator", + "translate_market_signals", + "translate_plan_delta", + "RunLog", + "LedgerSigner", + "sign_and_append", +] diff --git a/deltax_forge_cross/adapters/__init__.py b/deltax_forge_cross/adapters/__init__.py new file mode 100644 index 0000000..cd56da4 --- /dev/null +++ b/deltax_forge_cross/adapters/__init__.py @@ -0,0 +1 @@ +"""Starter adapters for DeltaX-Forge interoperability.""" diff --git a/deltax_forge_cross/adapters/price_feed_adapter.py b/deltax_forge_cross/adapters/price_feed_adapter.py new file mode 100644 index 0000000..5719a63 --- /dev/null +++ b/deltax_forge_cross/adapters/price_feed_adapter.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from deltax_forge_cross.dsl import MarketSignal, StrategyDelta + + +def translate_market_signals(strategy_delta: StrategyDelta) -> dict: + """Translate MarketSignal objects into a simple feed dictionary.""" + feeds = {} + for s in strategy_delta.signals: + feeds[s.symbol] = { + "price": s.price, + "liquidity": s.liquidity, + "latency_proxy": s.latency_proxy, + } + return feeds diff --git a/deltax_forge_cross/adapters/venue_execution_adapter.py b/deltax_forge_cross/adapters/venue_execution_adapter.py new file mode 100644 index 0000000..70573a2 --- /dev/null +++ b/deltax_forge_cross/adapters/venue_execution_adapter.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from typing import Dict + +from deltax_forge_cross.dsl import PlanDelta + + +def translate_plan_delta(plan_delta: PlanDelta) -> Dict[str, float]: + """Translate PlanDelta into a simplified execution map per venue. + This toy translator simply echoes hedge_updates for downstream execution. + """ + return dict(plan_delta.hedge_updates) diff --git a/deltax_forge_cross/core/central_curator.py b/deltax_forge_cross/core/central_curator.py new file mode 100644 index 0000000..105a569 --- /dev/null +++ b/deltax_forge_cross/core/central_curator.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from typing import Dict, List + + +class CentralCurator: + def __init__(self, venue_ids: List[str]): + self.venue_ids = venue_ids + + def coordinate(self, local_deltas: List[Dict[str, float]]) -> Dict[str, float]: + if not local_deltas: + return {} + # Simple average of deltas per asset across venues + keys = set().union(*local_deltas) # all asset symbols + shared: Dict[str, float] = {} + for k in keys: + total = sum(d.get(k, 0.0) for d in local_deltas) + shared[k] = total / len(local_deltas) + return shared diff --git a/deltax_forge_cross/core/local_venue_solver.py b/deltax_forge_cross/core/local_venue_solver.py new file mode 100644 index 0000000..610cf97 --- /dev/null +++ b/deltax_forge_cross/core/local_venue_solver.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from typing import Dict, List, Optional + +from deltax_forge_cross.dsl import StrategyDelta + + +class LocalVenueSolver: + def __init__(self, venue_id: str, assets: Optional[List[str]] = None): + self.venue_id = venue_id + self.assets = assets or [] + + def compute_local_delta(self, strategy_delta: StrategyDelta) -> Dict[str, float]: + # Very simple heuristic: divide delta_budget evenly across assets present in strategy + asset_symbols = [a.symbol for a in strategy_delta.assets] if strategy_delta.assets else [] + if not asset_symbols: + return {} + per = strategy_delta.delta_budget / len(asset_symbols) + return {sym: per for sym in asset_symbols} diff --git a/deltax_forge_cross/dsl.py b/deltax_forge_cross/dsl.py new file mode 100644 index 0000000..1cb3c34 --- /dev/null +++ b/deltax_forge_cross/dsl.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import List, Dict + + +@dataclass +class Asset: + symbol: str + kind: str # e.g., 'Equity' + + +@dataclass +class Leg: + asset: Asset + target_delta: float + + +@dataclass +class MarketSignal: + symbol: str + price: float + liquidity: float + latency_proxy: float + + +@dataclass +class AuditLogEntry: + timestamp: float + actor: str + action: str + note: str + + +@dataclass +class PlanDelta: + hedge_updates: Dict[str, float] # asset_symbol -> delta + authority: str + audit_log: List[AuditLogEntry] = field(default_factory=list) + + def to_dict(self) -> Dict: + return { + "hedge_updates": self.hedge_updates, + "authority": self.authority, + "audit_log": [ + {"timestamp": e.timestamp, "actor": e.actor, "action": e.action, "note": e.note} + for e in self.audit_log + ], + } + + +@dataclass +class StrategyDelta: + delta_budget: float + vega_budget: float + gamma_budget: float + pnl_target: float + assets: List[Asset] = field(default_factory=list) + legs: List[Leg] = field(default_factory=list) + signals: List[MarketSignal] = field(default_factory=list) diff --git a/deltax_forge_cross/ledger.py b/deltax_forge_cross/ledger.py new file mode 100644 index 0000000..dd5085f --- /dev/null +++ b/deltax_forge_cross/ledger.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +import json +import time +from typing import Any + + +class LedgerSigner: + def __init__(self, key: str): + self.key = key + + def sign(self, message: str) -> str: + import hmac + import hashlib + return hmac.new(self.key.encode("utf-8"), message.encode("utf-8"), hashlib.sha256).hexdigest() + + def verify(self, message: str, signature: str) -> bool: + return self.sign(message) == signature + + +def sign_and_append(plan_delta: Any, signer: LedgerSigner, log_path: str = "ledger.log") -> bool: + # Convert plan_delta to a portable dict if possible + if hasattr(plan_delta, "to_dict"): + payload = plan_delta.to_dict() + elif isinstance(plan_delta, dict): + payload = plan_delta + else: + payload = {"repr": repr(plan_delta)} + + payload_str = json.dumps(payload, sort_keys=True) + signature = signer.sign(payload_str) + entry = {"timestamp": time.time(), "payload": payload, "signature": signature} + with open(log_path, "a", encoding="utf-8") as f: + f.write(json.dumps(entry) + "\n") + return True diff --git a/deltax_forge_cross/logging.py b/deltax_forge_cross/logging.py new file mode 100644 index 0000000..a6fb846 --- /dev/null +++ b/deltax_forge_cross/logging.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +import json +import time + + +class RunLog: + def __init__(self, path: str = "deltax_run.log"): + self.path = path + + def log(self, entry: dict) -> None: + with open(self.path, "a", encoding="utf-8") as f: + payload = { + "ts": time.time(), + "event": entry, + } + f.write(json.dumps(payload) + "\n") + + @staticmethod + def replay(path: str): + with open(path, "r", encoding="utf-8") as f: + for line in f: + if not line.strip(): + continue + try: + obj = json.loads(line) + yield obj + except json.JSONDecodeError: + continue diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4fb8734 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,18 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "idea149-deltax-forge-cross" +version = "0.1.0" +description = "MVP for cross-venue delta-driven execution planning with a canonical DSL and two-tier solver" +requires-python = ">=3.9" +license = { text = "MIT" } +readme = "README.md" +dependencies = [ + "pydantic>=1.10,<2", + "pytest; python_version>='3.9'", +] + +[tool.setuptools.packages.find] +where = ["."] diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..dca1007 --- /dev/null +++ b/test.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Build and test DeltaX-Forge project +# Steps: +# 1) Build sdist+wheel +# 2) Install the produced wheel into a clean environment +# 3) Run test suite + +echo "[CI] Building distribution..." +python3 -m build + +echo "[CI] Installing built wheel..." +# Find the produced wheel and install it +WHEEL=$(ls dist/*.whl | head -n 1) +if [[ -z "$WHEEL" ]]; then + echo "No wheel found in dist/; aborting." >&2 + exit 1 +fi +python3 -m pip install --upgrade pip +python3 -m pip install "$WHEEL" + +echo "[CI] Running tests..." +pytest -q diff --git a/tests/test_deltax.py b/tests/test_deltax.py new file mode 100644 index 0000000..bfc4cf7 --- /dev/null +++ b/tests/test_deltax.py @@ -0,0 +1,70 @@ +import time +from deltax_forge_cross.dsl import Asset, MarketSignal, StrategyDelta, PlanDelta, AuditLogEntry +from deltax_forge_cross.core.local_venue_solver import LocalVenueSolver +from deltax_forge_cross.core.central_curator import CentralCurator +from deltax_forge_cross.adapters.price_feed_adapter import translate_market_signals +from deltax_forge_cross.adapters.venue_execution_adapter import translate_plan_delta +from deltax_forge_cross.logging import RunLog +from deltax_forge_cross.ledger import LedgerSigner, sign_and_append + + +def test_dsl_creation(): + a1 = Asset(symbol="AAPL", kind="Equity") + a2 = Asset(symbol="MSFT", kind="Equity") + ms = MarketSignal(symbol="AAPL", price=150.0, liquidity=0.9, latency_proxy=0.01) + strategy = StrategyDelta( + delta_budget=10.0, + vega_budget=2.0, + gamma_budget=1.0, + pnl_target=1.0, + assets=[a1, a2], + signals=[ms], + ) + assert strategy.delta_budget == 10.0 + assert len(strategy.assets) == 2 + + +def test_local_and_central_coherence(): + a1 = Asset(symbol="AAPL", kind="Equity") + a2 = Asset(symbol="MSFT", kind="Equity") + strategy = StrategyDelta(delta_budget=10.0, vega_budget=2.0, gamma_budget=1.0, pnl_target=1.0, + assets=[a1, a2]) + solver1 = LocalVenueSolver("VENUE1", assets=[a1.symbol, a2.symbol]) + solver2 = LocalVenueSolver("VENUE2", assets=[a1.symbol, a2.symbol]) + d1 = solver1.compute_local_delta(strategy) + d2 = solver2.compute_local_delta(strategy) + import pytest + curator = CentralCurator(["VENUE1", "VENUE2"]) + shared = curator.coordinate([d1, d2]) + assert shared["AAPL"] == pytest.approx(5.0) + assert shared["MSFT"] == pytest.approx(5.0) + + +def test_log_replay(): + log = RunLog("test_run.log") + log.log({"event": "start"}) + # Replay should yield 1+ entries; we simply verify that it yields a dict-like payload + entries = list(RunLog.replay("test_run.log")) + assert len(entries) >= 1 + + +def test_adapter_translation_and_governance_signing(): + a1 = Asset(symbol="AAPL", kind="Equity") + strategy = StrategyDelta(delta_budget=10.0, vega_budget=2.0, gamma_budget=1.0, pnl_target=1.0, + assets=[a1]) + # price feed translation + ms = MarketSignal(symbol="AAPL", price=150.0, liquidity=0.8, latency_proxy=0.02) + strategy.signals = [ms] + feeds = translate_market_signals(strategy) + assert "AAPL" in feeds + + # plan delta translation + plan = PlanDelta(hedge_updates={"AAPL": 5.0}, authority="tester") + exec_map = translate_plan_delta(plan) + assert exec_map["AAPL"] == 5.0 + + # governance signing + signer = LedgerSigner("secretkey") + payload = plan.to_dict() if hasattr(plan, 'to_dict') else plan.__dict__ + sig = signer.sign(str(payload)) + assert signer.verify(str(payload), sig)