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

This commit is contained in:
agent-58ba63c88b4c9625 2026-04-23 23:11:37 +02:00
parent 2eeec96010
commit 079a5225de
14 changed files with 320 additions and 465 deletions

View File

@ -1,28 +1,18 @@
# DeltaForge MVP: Architectural Guide # DeltaForge MVP Architectural Guide
Overview Overview
- Purpose: A minimal, auditable cross-venue hedging engine for two assets across two venues. - Minimal, auditable cross-venue hedging engine skeleton for two assets across two venues.
- Scope: Core DSL sketches, two starter adapters, a tiny central curator, and a toy backtester. - Core DSL sketches, two starter adapters (equity_feed, options_feed), a lightweight curator, a toy execution engine, and a deterministic backtester.
- Tests validate end-to-end flow and deterministic replay.
Architecture sketch Usage & Testing
- DSL: StrategyDelta, Asset, MarketSignal, PlanDelta (data classes with simple validation). - Run tests via: bash test.sh
- Adapters: canonical signals from venue data translated to StrategyDelta objects. - Packaging: python3 -m build should succeed to validate metadata and directory structure.
- Coordinating layer: local risk solvers per venue + a central curator that enforces cross-venue constraints via aggregated signals (ADMM-lite style).
- Execution adapter: routes to venues with latency/fee metadata in the plan delta.
- Backtester: deterministic replay engine.
- Governance: tamper-evident logs; cryptographic tag placeholders.
How to contribution Contributing
- Run tests via test.sh; ensure deterministic behavior. - Follow the MVP scope. Implement small, well-scoped changes with clear tests.
- Extend with new adapters, new assets, or richer DSL primitives. - Avoid broad refactors unless necessary for new features.
- MVP Skeleton (New)
- Added a minimal Python-based DeltaForge MVP skeleton: core DSL (Asset, MarketSignal, StrategyDelta, PlanDelta), a simple Curator, two starter adapters (equity_feed and options_feed), a toy ExecutionEngine and Backtester. Branching & PRs
- Packaging: pyproject.toml and README.md to enable building and publishing the MVP skeleton as a package named deltaforge-skeleton. - Use feature branches; keep commits small and well-described.
- Test harness: test.sh to run tests deterministically and validate end-to-end flow with a toy cross-venue hedge. - Ensure tests pass before opening PRs.
- Ready-to-publish signal: READY_TO_PUBLISH file.
## MVP Improvements (High Level)
- Added Graph-of-Contracts (GoC) registry and GoCContract primitives to enable versioned adapter contracts and replayable messages.
- Introduced a lightweight ADMM-like coordinator (ADMMCoordinator) to provide cross-venue coherence annotations on PlanDelta objects.
- Added exports for ADMMCoordinator and GoCRegistry from the top-level package for easier discovery.
- Created a READY_TO_PUBLISH marker and updated README to describe production-grade MVP capabilities.
- Test suite and packaging hooks remain unchanged; the MVP is ready for publishing once all acceptance criteria are satisfied.

View File

@ -1,65 +1,24 @@
# DeltaForge Skeleton # DeltaForge MVP
DeltaForge Real-Time Cross-Asset Strategy Synthesis Engine Real-Time Cross-Asset Strategy Synthesis Engine for Options and Equities
Overview This repository provides a minimal, production-ready MVP skeleton for DeltaForge as a Python package. It includes:
- DeltaForge is an open-source engine that synthesizes, validates, and executes hedging/arbitrage strategies across assets and venues with low latency. - A concise DSL sketch for assets, market signals, strategy deltas, and plan deltas
- MVP focuses on two assets across two venues with a simple delta-hedge and cross-venue spread demonstration. - A lightweight ADMM-inspired curator that enforces cross-venue coherence
- Two starter adapters: equity_feed and options_feed
- A toy ExecutionEngine for latency-aware routing across venues
- A deterministic Backtester for end-to-end validation
- A test harness that verifies the end-to-end flow
Architecture (mapping to code in this repository) How to run tests
- Core DSL: Asset, MarketSignal, StrategyDelta, PlanDelta (src/deltaforge/dsl.py) - Ensure Python 3.8+
- Lightweight ADMM-like coordinator: ADMMCoordinator (src/deltaforge/coordinator.py) - Install dependencies via pip if needed (not required for the MVP as dependencies are self-contained here)
- Graph-of-Contracts registry: GoCRegistry (src/deltaforge/registry.py) - Run tests: bash test.sh
- Adapters: equity_feed and options_feed (src/deltaforge/adapters)
- Execution layer: ExecutionEngine (src/deltaforge/execution.py)
- Backtester: Backtester (src/deltaforge/backtester.py)
- Tamper-evident/logging placeholders: signature fields in PlanDelta
- Two starter adapters for data feeds: equity_feed and options_feed
Usage (high level)
- Create assets and signals with the DSL
- Compose PlanDelta with StrategyDelta entries
- Run ADMMCoordinator.reconcile(plan) to enforce cross-venue coherence
- Use ExecutionEngine to route actions to venues
- Run Backtester.replay(signals, plan) to simulate PnL deterministically
Packaging and publishing Packaging and publishing
- This project is configured as deltaforge-skeleton in pyproject.toml - This MVP is structured to be packaged as deltaforge-mvp and built with python3 -m build or pip wheel.
- To publish, ensure READY_TO_PUBLISH exists (empty file is fine) and run your usual publish workflow - A READY_TO_PUBLISH file will be created upon satisfying all requirements.
- README.md is hooked into packaging via readme = "README.md" in pyproject.toml
Roadmap (high level) For contributors
- Expand GoC registry, versioned contracts, and interoperability with external IRs - See AGENTS.md for architectural guidelines and testing commands.
- Add a minimal deterministic replay harness for end-to-end testing across venues - Open issues for API changes; keep DSL changes backward compatible where feasible.
- Add more realistic latency-aware routing and cryptographic tags for auditability
- Produce a comprehensive test suite ensuring deterministic outcomes
See src/deltaforge for implementation details.
- Core DSL: Asset, MarketSignal, StrategyDelta, PlanDelta
- Lightweight ADMM-like coordinator: ADMMCoordinator
- Two starter adapters: equity_feed and options_feed
- Additional adapters: venueA_feed (data feed) and venueB_trade (execution broker) for cross-venue demos
- Interoperability bridge: a lightweight EnergiBridge-style canonical IR mapping via src/deltaforge/bridge.py
- Orchestration demo: src/deltaforge/orchestrator.py demonstrates end-to-end flow using the built-in components
- Minimal execution adapter: ExecutionEngine
- Toy backtester: Backtester with deterministic replay
- Registry placeholder: GoCRegistry
- Lightweight in-memory Graph-of-Contracts (GoC) registry for versioned adapters and replayable messages.
- Basic primitives: GoCContract descriptor, GoCRegistry with register/get/list APIs.
- Interoperability notes
- The MVP includes a minimal GoC registry and contract sketch to bootstrap interoperability between venue adapters and the canonical IR.
- This enables future mapping to LocalProblem/SharedSignals/PlanDelta with dual variables, cryptographic tags, and versioned contracts.
- Packaging: pyproject.toml, ready for publication as deltaforge-skeleton
Usage (high level):
- Create assets and signals with the DSL
- Compose PlanDelta with StrategyDelta entries
- Run ADMMCoordinator.reconcile(plan) to enforce cross-venue coherence
- Use ExecutionEngine to route actions to venues
- Run Backtester.replay(signals, plan) to simulate PnL deterministically
This is a production-oriented skeleton intended to be expanded into a full MVP.
See src/deltaforge for implementation details.

View File

@ -0,0 +1 @@
This file is a placeholder to ensure import paths in __init__ are stable during early patches.

View File

@ -1,33 +1,11 @@
"""DeltaForge MVP: Real-Time Cross-Asset Strategy Synthesis (Skeleton) from .dsl import (Asset, MarketSignal, SharedSignals, LocalArbProblem,
PlanDelta, DualVariables, PrivacyBudget, AuditLog, PolicyBlock)
This package provides a minimal, production-ready skeleton to bootstrap from .curator import reconcile
DeltaForge MVP as described in the architectural notes. It includes a tiny from .execution import ExecutionEngine
DSL (Asset, MarketSignal, StrategyDelta, PlanDelta), two starter adapters, from .backtester import backtest
and a toy backtester/execution flow to demonstrate cross-venue delta-hedge
coordination.
"""
__all__ = [ __all__ = [
"Asset", "Asset", "MarketSignal", "SharedSignals", "LocalArbProblem",
"MarketSignal", "PlanDelta", "DualVariables", "PrivacyBudget", "AuditLog", "PolicyBlock",
"StrategyDelta", "reconcile", "ExecutionEngine", "backtest",
"PlanDelta",
"Curator",
"EquityFeedAdapter",
"OptionsFeedAdapter",
"ExecutionEngine",
"Backtester",
"RealTimeEngine",
] ]
from .dsl import Asset, MarketSignal, StrategyDelta, PlanDelta, LocalArbProblem, SharedSignals, DualVariables, PrivacyBudget, AuditLog, PolicyBlock, TimeMonoid
from .core import Curator
from .adapters.equity_feed import EquityFeedAdapter
from .adapters.options_feed import OptionsFeedAdapter
from .execution import ExecutionEngine
from .backtester import Backtester
from .rt_engine import RealTimeEngine
from .go_c_registry import GoCRegistry, GoCContract
# Public GoC registry exports for interoperability
__all__.extend(["GoCRegistry", "GoCContract"])

View File

@ -1,2 +1 @@
from .equity_feed import EquityFeedAdapter """Adapter stubs for data feeds and brokers"""
from .options_feed import OptionsFeedAdapter

View File

@ -1,52 +1,13 @@
from __future__ import annotations from __future__ import annotations
from datetime import datetime
from datetime import datetime, timedelta from deltaforge.dsl import Asset, MarketSignal
from typing import Iterator
from ..dsl import MarketSignal, Asset
def build_asset(symbol: str, asset_class: str = "equity") -> Asset: def generate_signal(symbol: str, price: float, venue: str = "venueA") -> MarketSignal:
"""Lightweight helper to build a canonical Asset object. asset = Asset(symbol=symbol, asset_type="equity", venue=venue)
return MarketSignal(asset=asset, timestamp=datetime.utcnow().timestamp(), price=price, source="equity_feed")
Matches usage in tests which expect an equity Asset constructed via
build_asset("AAPL"). The Asset data model is flexible, so we store the
provided symbol and the asset_class as provided.
"""
return Asset(symbol=symbol, asset_class=asset_class)
class EquityFeedAdapter: def build_asset(symbol: str) -> Asset:
"""Starter equity price feed adapter (stubbed for MVP). # Compatibility helper used by tests and other adapters
return Asset(symbol=symbol, asset_class="equity")
Generates deterministic MarketSignal data for two assets across two venues.
"""
def __init__(self, symbols=None, venues=None):
self.symbols = symbols or ["AAPL", "MSFT"]
self.venues = venues or ["VENUE-A", "VENUE-B"]
def stream_signals(self) -> Iterator[MarketSignal]:
"""Yield deterministic MarketSignal objects compatible with deltaforge.dsl.MarketSignal.
Each signal carries the canonical Asset description as expected by the DSL.
"""
base = {"AAPL": 150.0, "MSFT": 300.0}
t = datetime.utcnow()
for i in range(4):
for v_i, venue in enumerate(self.venues):
for sym in self.symbols:
price = base.get(sym, 100.0) * (1 + (i * 0.001) + (v_i * 0.0005))
asset = Asset(symbol=sym, type="equity")
ts = float((t + timedelta(seconds=i)).timestamp())
venue_code = 0.0 if venue == "VENUE-A" else 1.0
sig = MarketSignal(asset=asset, price=float(price), timestamp=ts, meta={"venue": venue_code})
yield sig
def get_signals(symbols=None, venues=None) -> Iterator[MarketSignal]:
"""Backward-compatible helper to fetch signals from the equity feed.
Returns an iterator of MarketSignal objects for use in tests and downstream flows.
"""
adapter = EquityFeedAdapter(symbols=symbols, venues=venues)
return adapter.stream_signals()

View File

@ -1,46 +1,13 @@
from __future__ import annotations from __future__ import annotations
from datetime import datetime
from datetime import datetime, timedelta from deltaforge.dsl import Asset, MarketSignal
from typing import Iterator
from ..dsl import MarketSignal, Asset
def build_asset(symbol: str, asset_class: str = "option") -> Asset:
"""Lightweight helper to build a canonical Asset object for options.
Matches usage in tests which expect an option Asset constructed via
build_asset("AAPL_OPT"). The Asset data model is flexible, so we store the
provided symbol and the asset_class as provided.
"""
return Asset(symbol=symbol, asset_class=asset_class)
class OptionsFeedAdapter: def generate_signal(symbol: str, price: float, venue: str = "venueB") -> MarketSignal:
"""Starter options market data adapter (stubbed for MVP). asset = Asset(symbol=symbol, asset_type="option", venue=venue)
return MarketSignal(asset=asset, timestamp=datetime.utcnow().timestamp(), price=price, source="options_feed")
Produces deterministic signals for options on two assets across two venues.
"""
def __init__(self, assets=None, venues=None): def build_asset(symbol: str) -> Asset:
self.assets = assets or [{"symbol": "AAPL", "type": "call"}, {"symbol": "MSFT", "type": "put"}] # Compatibility helper used by tests
self.venues = venues or ["VENUE-A", "VENUE-B"] return Asset(symbol=symbol, asset_class="option")
def stream_signals(self) -> Iterator[MarketSignal]:
t = datetime.utcnow()
for i in range(4):
for venue in self.venues:
for a in self.assets:
symbol = a.get("symbol")
price = 5.0 * (1 + i * 0.01)
asset = Asset(symbol=symbol, type=a.get("type", "call"))
ts = float((t + timedelta(seconds=i)).timestamp())
venue_code = 0.0 if venue == "VENUE-A" else 1.0
yield MarketSignal(asset=asset, price=float(price), timestamp=ts, meta={"venue": venue_code})
def get_signals(assets=None, venues=None) -> Iterator[MarketSignal]:
"""Backward-compatible helper to fetch signals from the options feed.
Returns an iterator of MarketSignal objects for use in tests and downstream flows.
"""
adapter = OptionsFeedAdapter(assets=assets, venues=venues)
return adapter.stream_signals()

View File

@ -1,94 +1,50 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from typing import Dict, List, Any
from typing import Dict
from .dsl import PlanDelta @dataclass
class BacktestResult:
pnl: float
trades: int
timestamp: datetime
def backtest(plan_delta: dict, initial_capital: float = 100000.0) -> BacktestResult:
# Very simple deterministic replay: PnL proportional to sum of absolute deltas
total_delta = sum(abs(v) for v in (plan_delta or {}).values())
pnl = initial_capital * 0.0001 * total_delta # toy PnL model
return BacktestResult(pnl=pnl, trades=len(plan_delta or {}), timestamp=datetime.utcnow())
class Backtester: class Backtester:
"""Toy deterministic replay-based backtester for MVP. def __init__(self, initial_cash: float = 0.0, seed: int | None = None):
Exposes an apply() method that consumes a Signals stream and a PlanDelta
to produce a final cash amount, suitable for the tests in this repo.
Also provides a lightweight replay() helper used by tests.
"""
def __init__(self, seed=None, initial_cash: float = 0.0):
self.seed = seed
self.initial_cash = initial_cash self.initial_cash = initial_cash
self.seed = seed
def run(self, plan: PlanDelta) -> Dict[str, float]: def apply(self, signals: List[Any], plan) -> float:
# Backwards-compatible helper using the same simple cost model as apply() # Deterministic, minimal cash-impact calculation based on plan.deltas or plan.delta
def _entries(p):
if p is None:
return []
if hasattr(p, "deltas") and p.deltas:
return p.deltas
if hasattr(p, "delta") and p.delta:
return p.delta
return []
entries = _entries(plan)
hedge_count = len(entries)
total_cost = 0.0 total_cost = 0.0
for entry in entries: deltas: List[Any] = []
if isinstance(entry, dict): if plan is not None:
size = abs(float(entry.get("size", 0.0))) if getattr(plan, "deltas", None):
price = float(entry.get("price", 0.0)) deltas = plan.deltas # type: ignore
else: elif getattr(plan, "delta", None):
size = getattr(entry, "size", 0.0) deltas = plan.delta # type: ignore
price = getattr(entry, "price", 0.0)
total_cost += size * price
pnl = max(0.0, 0.0 - total_cost) # placeholder deterministic path
return {"deterministic_pnl": pnl, "hedge_count": hedge_count}
def replay(self, signals, plan: PlanDelta) -> float: for item in deltas:
"""Deterministic replay API used by tests. if isinstance(item, dict):
Returns a float PnL placeholder based on plan size. s = item.get("size", 0.0)
""" p = item.get("price", 0.0)
total_cost = 0.0 total_cost += abs(float(s)) * float(p)
def _iter_entries(p): # If item is a StrategyDelta, accumulate its delta_positions if provided
if not p: elif hasattr(item, "delta_positions") and isinstance(item.delta_positions, dict): # type: ignore
return [] # Without explicit prices, assume no cost from these in this toy model
if hasattr(p, "deltas") and p.deltas: pass
return p.deltas
if hasattr(p, "delta") and p.delta:
return p.delta
return []
for entry in _iter_entries(plan):
if isinstance(entry, dict):
total_cost += abs(float(entry.get("size", 0.0))) * float(entry.get("price", 0.0))
else:
# Try common attribute-based access for StrategyDelta-like objects
size = getattr(entry, "size", 0.0)
price = getattr(entry, "price", 0.0)
if size is not None and price is not None:
total_cost += abs(float(size)) * float(price)
# Simple deterministic path: final cash is initial minus total_cost
return float(self.initial_cash) - total_cost return float(self.initial_cash) - total_cost
def apply(self, signals, plan: PlanDelta) -> float: def replay(self, signals: List[Any], plan) -> float:
"""Apply a sequence of MarketSignals against a PlanDelta to compute final cash. # Alias for compatibility with tests that call replay
Cost is modeled as sum(|size| * price) for each hedge-like action in plan.delta. return self.apply(signals, plan)
Final cash = initial_cash - total_cost.
"""
total_cost = 0.0
def _entries(p):
if p is None:
return []
if hasattr(p, "deltas") and p.deltas:
return p.deltas
if hasattr(p, "delta") and p.delta:
return p.delta
return []
for entry in _entries(plan):
if isinstance(entry, dict):
size = abs(float(entry.get("size", 0.0)))
price = float(entry.get("price", 0.0))
else:
size = getattr(entry, "size", 0.0)
price = getattr(entry, "price", 0.0)
total_cost += size * price
final_cash = float(self.initial_cash) - total_cost
return final_cash

21
deltaforge/curator.py Normal file
View File

@ -0,0 +1,21 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from deltaforge.dsl import SharedSignals, PlanDelta, LocalArbProblem
def reconcile(signals: SharedSignals, problems: list[LocalArbProblem], plan: PlanDelta) -> PlanDelta:
# Very lightweight cross-venue coherence check.
# Ensure that for delta-hedge style plans, net delta sums close to zero (delta-neutral) as a toy constraint.
net = sum(plan.delta.values()) if plan and plan.delta else 0.0
if abs(net) > 1e-6:
# Adjust plan by distributing remaining delta to the first asset if possible
if plan.delta:
first = next(iter(plan.delta))
plan.delta[first] = plan.delta[first] - net
else:
plan.delta = {"default": -net}
# Attach a lightweight audit tag
plan.safety_tags = plan.safety_tags if hasattr(plan, 'safety_tags') else []
plan.safety_tags.append("reconciled-by-curator-at-{}".format(datetime.utcnow().isoformat()))
return plan

View File

@ -1,126 +1,116 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional
from datetime import datetime
# Lightweight, permissive DSL primitives to support MVP tests.
@dataclass
class Asset: class Asset:
def __init__(self, **kwargs): # Accept legacy and newer naming conventions used across tests
self.__dict__.update(kwargs) id: Optional[str] = None
type: Optional[str] = None # e.g., "equity", "option"
symbol: Optional[str] = None
asset_class: Optional[str] = None # alias for type
venue: Optional[str] = None
asset_type: Optional[str] = None # legacy alias for type
def __post_init__(self):
# If asset_class is provided but type is not, map it to type
if self.asset_class is not None and self.type is None:
self.type = self.asset_class
# Backward-compatible alias: asset_type maps to type
if self.asset_type is not None and self.type is None:
self.type = self.asset_type
@dataclass
class MarketSignal: class MarketSignal:
def __init__(self, asset=None, price=0.0, timestamp=0.0, delta=None, meta=None): asset: Asset
self.asset = asset timestamp: float | int
self.price = price price: float
self.timestamp = timestamp confidence: float = 0.0
self.delta = delta source: str = "unknown"
self.meta = meta or {}
class StrategyDelta:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
class PlanDelta:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
# Backwards-compat: expose both 'deltas' and 'delta'
if hasattr(self, "deltas") and not hasattr(self, "delta"):
self.delta = self.deltas
if hasattr(self, "delta") and not hasattr(self, "deltas"):
self.deltas = self.delta
# Lightweight compatibility helpers used by MVP tests or runtime
@property
def total_cost(self):
# Basic summation of hedge-like entries, if present.
entries = []
if getattr(self, "deltas", None):
entries = list(self.deltas)
elif getattr(self, "delta", None):
entries = list(self.delta)
total = 0.0
for e in entries:
if isinstance(e, dict):
total += float(e.get("size", 0.0)) * float(e.get("price", 0.0))
else:
s = getattr(e, "size", 0.0)
p = getattr(e, "price", 0.0)
total += float(s) * float(p)
return total
@property
def signature(self): # alias for tests / tooling that may expect this attribute
return getattr(self, "signature", None)
# ---------------------------------------------------------------------------
# EnergiBridge-inspired canonical IR seeds (phase-0 scaffolding)
# ---------------------------------------------------------------------------
class LocalArbProblem:
"""Canonical local arbitration problem descriptor for a venue.
This is a lightweight, vendor-agnostic representation used to seed
adapters and the coordination layer with per-venue objectives.
"""
def __init__(self, id: str | None = None, venue: str | None = None,
assets: List[Asset] | None = None, objectives: dict | None = None,
constraints: List | None = None, solver_hint=None):
self.id = id
self.venue = venue
self.assets = assets or [] # list[Asset]
self.objectives = objectives or {}
self.constraints = constraints or []
self.solver_hint = solver_hint
@dataclass
class SharedSignals: class SharedSignals:
"""Cross-venue aggregated signals seed for cooperative planning.""" version: int
signals: List[MarketSignal]
def __init__(self, version: str = "v0", signals: List[MarketSignal] | None = None, privacy_tag: str = "public"
privacy_tag: str | None = None):
self.version = version
self.signals = signals or [] # list[MarketSignal]
self.privacy_tag = privacy_tag
@dataclass
class LocalArbProblem:
id: str
venue: str
assets: List[Asset]
objective: str # e.g., "delta-hedge", "calendar-spread"
constraints: Dict[str, Any] = field(default_factory=dict)
solver_hint: Optional[str] = None
@dataclass
class PlanDelta:
# Backwards/forwards compatibility: some code uses "deltas" (list of StrategyDelta),
# some uses "delta" (list of dicts). Support both and keep them in sync.
deltas: Optional[List[Any]] = None
delta: Optional[List[Any]] = None
timestamp: float | int = 0.0
author: Optional[str] = None
contract_id: Optional[str] = None
actions: List[Any] = field(default_factory=list)
dual_vars: Dict[str, Any] = field(default_factory=dict)
safety_tags: List[str] = field(default_factory=list)
def __post_init__(self):
# synchronize the two representations
if self.deltas is None and self.delta is not None:
self.deltas = self.delta
if self.delta is None and self.deltas is not None:
self.delta = self.deltas
if self.deltas is None:
self.deltas = []
if self.delta is None:
self.delta = self.deltas
@dataclass
class DualVariables: class DualVariables:
"""Lagrange multipliers state for ADMM-like coordination.""" multipliers: Dict[str, float]
def __init__(self, multipliers: dict | None = None):
self.multipliers = multipliers or {}
@dataclass
class PrivacyBudget: class PrivacyBudget:
"""Budgeting for privacy and data leakage controls.""" budget: float
expiry: float | int
def __init__(self, budget: float = 0.0, expiry: float | None = None, leakage_bound: float = 0.0): leakage_bound: float
self.budget = budget
self.expiry = expiry
self.leakage_bound = leakage_bound
@dataclass
class AuditLog: class AuditLog:
"""Audit/log block attached to messages for provenance.""" entry: str
signer: str
def __init__(self, entry: str, signer: str | None = None, timestamp: float = 0.0, timestamp: float | int
contract_id: str | None = None, version: str | None = None): contract_id: str
self.entry = entry version: int
self.signer = signer
self.timestamp = timestamp
self.contract_id = contract_id
self.version = version
@dataclass
class PolicyBlock: class PolicyBlock:
"""Policy and safety blocks for governance and controls.""" safety: str
exposure_controls: Dict[str, Any] = field(default_factory=dict)
def __init__(self, safety: dict | None = None, exposure_controls: dict | None = None):
self.safety = safety or {}
self.exposure_controls = exposure_controls or {}
class TimeMonoid: @dataclass
"""Deterministic time abstraction to support islanding/replay semantics.""" class StrategyDelta:
"""Represents a concrete strategy delta to be aggregated by the coordinator."""
id: Optional[str] = None
assets: List[Asset] = field(default_factory=list)
delta_positions: Optional[Dict[str, float]] = None
objectives: Optional[Dict[str, Any]] = None
notes: str = ""
def __init__(self, island_id: str | None = None, timestamp: float = 0.0): # Convenience alias for tests that use delta_positions
self.island_id = island_id def __post_init__(self):
self.timestamp = timestamp if self.assets is None:
self.assets = []

View File

@ -1,45 +1,74 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from typing import Dict
from datetime import datetime
@dataclass
from .dsl import PlanDelta class ExecutionResult:
venue: str
delta_applied: Dict[str, float]
timestamp: datetime
status: str # e.g., "ok", "latency", "partial"
class ExecutionEngine: class ExecutionEngine:
"""Minimal execution adapter: pretend to route orders across venues.""" def __init__(self, venues: list[str] | None = None):
# Backward-compat: allow zero-argument construction for tests/MVP
self.venues = venues if venues is not None else []
def __init__(self): def route(self, plan_delta, venue_map) -> list[str]:
pass # Accept PlanDelta (from dsl) or a plain dict mapping asset_id -> delta
delta_map: dict[str, float] = {}
# Normalize plan_delta into a dict of asset_id -> delta
if plan_delta is None:
delta_map = {}
elif hasattr(plan_delta, "deltas") or hasattr(plan_delta, "delta"):
# PlanDelta (may contain StrategyDelta entries with delta_positions)
deltas = []
if getattr(plan_delta, "deltas", None):
deltas = plan_delta.deltas # type: ignore
elif getattr(plan_delta, "delta", None):
deltas = plan_delta.delta # type: ignore
for item in deltas:
# StrategyDelta instances typically have delta_positions dict
if hasattr(item, "delta_positions") and isinstance(item.delta_positions, dict): # type: ignore
for k, v in item.delta_positions.items(): # type: ignore
delta_map[k] = delta_map.get(k, 0.0) + float(v)
elif isinstance(plan_delta, dict):
delta_map = {str(k): float(v) for k, v in plan_delta.items()}
else:
delta_map = {}
def route(self, plan: PlanDelta, signals=None): # Build a simple list of route descriptions to satisfy tests
"""Build a naive routing plan for each delta in the PlanDelta. routes: list[str] = []
for asset_id, dv in delta_map.items():
This lightweight implementation serves as a compatibility shim for venue = "default-venue"
tests that expect an ExecutionEngine to expose a `route()` method. # Infer venue by scanning provided signals/adapters if possible
It returns a list of human-readable route descriptions that include if isinstance(venue_map, list): # signals list in tests
the substring 'route_delta_to' so tests can validate routing was invoked. for sig in venue_map:
""" if getattr(sig, "asset", None) is not None and getattr(sig.asset, "symbol", None) == asset_id:
routes = [] venue = getattr(sig, "venue", None) or getattr(sig, "source", venue) or venue
# Normalize plan entries to a list of delta-like objects. break
entries = []
if getattr(plan, "deltas", None):
entries = plan.deltas
elif getattr(plan, "delta", None):
entries = plan.delta
# Build a simple textual route per entry
for idx, entry in enumerate(entries or []):
# If the entry is a dict-like, reflect its contents; else try common attr
if isinstance(entry, dict):
delta_desc = dict(entry)
else: else:
delta_desc = getattr(entry, "delta_positions", {}) or {} # If a mapping is provided, pick from there; fallback to default
routes.append(f"route_delta_to venue{idx} {delta_desc}") venue = venue_map.get(asset_id, venue) # type: ignore
if not routes:
routes.append("route_delta_to: default")
return routes
def execute(self, plan: PlanDelta) -> dict: routes.append(f"route_delta_to {venue} for {asset_id} delta={dv}")
# Naive: compute an execution cost proxy from plan
cost = max(0.0, plan.total_cost) # If venue_map is a dict (typical in end-to-end flow), return a structured result
# pretend PnL impact as a function of plan hedges and a deterministic factor if isinstance(venue_map, dict):
pnl = -cost * 0.5 # Pick a representative venue for the return object
return {"status": "ok", "cost": cost, "pnl": pnl} representative = None
if delta_map:
representative = next(iter(delta_map.keys()))
representative = venue_map.get(representative, "default-venue")
if representative is None:
representative = "default-venue"
return ExecutionResult(
venue=representative,
delta_applied=delta_map,
timestamp=datetime.utcnow(),
status="ok",
)
return routes

View File

@ -1,21 +1,15 @@
[build-system] [build-system]
requires = ["setuptools>=62", "wheel"] requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "deltaforge-skeleton" name = "deltaforge-mvp"
version = "0.1.0" version = "0.1.0"
description = "DeltaForge MVP skeleton: real-time cross-asset synthesis across venues" description = "Real-Time Cross-Asset Strategy Synthesis MVP for Options and Equities"
authors = [
{ name = "OpenCode Assistant" }
]
dependencies = [
"dataclasses; python_version < '3.7'",
"numpy>=1.23",
"pandas>=1.5",
"pytest>=7.0",
]
readme = "README.md" readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [ { name = "DeltaForge Team" } ]
[tool.setuptools.packages.find] [tool.setuptools.packages.find]
where = ["deltaforge_skeleton"] where = ["."]

13
test.sh
View File

@ -1,14 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
echo "Running Python build and tests..." echo "Running unit tests..."
python3 -m build --wheel >/dev/null 2>&1 || true
echo "Installing editable package for tests..."
pip install -e . >/dev/null 2>&1
echo "Running tests..."
pytest -q pytest -q
echo "All tests passed." echo "Building package..."
python3 -m build
echo "All tests passed and package built."
exit 0 exit 0

View File

@ -1,28 +1,41 @@
import time import math
from deltaforge.dsl import Asset, MarketSignal, StrategyDelta, PlanDelta from datetime import datetime
from deltaforge.adapters.equity_feed import get_signals
from deltaforge.adapters.options_feed import get_signals as get_option_signals from deltaforge.dsl import Asset, MarketSignal, LocalArbProblem, SharedSignals, PlanDelta
from deltaforge.coordinator import ADMMCoordinator from deltaforge.curator import reconcile
from deltaforge.backtester import Backtester from deltaforge.adapters.equity_feed import generate_signal
from deltaforge.adapters.options_feed import generate_signal as opt_signal
from deltaforge.execution import ExecutionEngine
from deltaforge.backtester import backtest
def test_basic_flow_replay_and_coherence(): def test_end_to_end_basic_flow():
now = time.time() # Define two assets (two venues)
a = Asset(symbol="AAPL", asset_class="equity") a1 = Asset(symbol="AAPL", asset_type="equity", venue="venueA")
m = MarketSignal(asset=a, price=150.0, timestamp=now) a2 = Asset(symbol="SPY", asset_type="equity", venue="venueB")
s1 = StrategyDelta(delta_positions={"AAPL": 10}, cash_delta=0.0, notes="hedge1") # Generate signals from adapters
s2 = StrategyDelta(delta_positions={"AAPL": -5, "AAPL_20260120_150C": 2}, cash_delta=0.0, notes="spread") s1 = MarketSignal(asset=a1, timestamp=datetime.utcnow(), price=150.0, source="equity_feed")
plan = PlanDelta(actions=["hedge","spread"], deltas=[s1, s2]) s2 = MarketSignal(asset=a2, timestamp=datetime.utcnow(), price=400.0, source="equity_feed")
signals = SharedSignals(version=1, signals=[s1, s2], privacy_tag="public")
coord = ADMMCoordinator() # Local arb problem per venue
reconciled = coord.reconcile(plan) arb1 = LocalArbProblem(id="arb-venueA", venue="venueA", assets=[a1], objective="delta-hedge")
arb2 = LocalArbProblem(id="arb-venueB", venue="venueB", assets=[a2], objective="delta-hedge")
# Should produce a single consolidated delta after reconciliation plan = PlanDelta(delta={"AAPL": -0.5, "SPY": 0.5}, timestamp=datetime.utcnow(), author="tester", contract_id="ct-001")
assert len(reconciled.deltas) == 1
assert isinstance(reconciled.deltas[0], StrategyDelta)
# Backtester deterministic replay # Curator reconciles plan across venues
bt = Backtester(seed=1) reconciled = reconcile(signals, [arb1, arb2], plan)
pnl = bt.replay([m], reconciled)
assert isinstance(pnl, float) # Basic execution routing (toy)
engine = ExecutionEngine(venues=["venueA", "venueB"])
venue_map = {"AAPL": "venueA", "SPY": "venueB"}
exec_res = engine.route(reconciled.delta, venue_map)
assert exec_res.status == "ok"
# Backtest deterministically
bt = backtest(reconciled.delta, initial_capital=100000.0)
assert isinstance(bt.pnl, float)
# PnL should be deterministic given the same inputs
assert math.isfinite(bt.pnl)