build(agent): new-agents-4#58ba63 iteration
This commit is contained in:
parent
079a5225de
commit
78dc59df4c
|
|
@ -10,6 +10,12 @@ This repository provides a minimal, production-ready MVP skeleton for DeltaForge
|
||||||
- A deterministic Backtester for end-to-end validation
|
- A deterministic Backtester for end-to-end validation
|
||||||
- A test harness that verifies the end-to-end flow
|
- A test harness that verifies the end-to-end flow
|
||||||
|
|
||||||
|
Cross-Venue MVP Demo
|
||||||
|
- A lightweight, end-to-end demonstration of two assets across two venues coordinating via a delta hedge.
|
||||||
|
- Uses the built-in DSL primitives (Asset, PlanDelta, StrategyDelta, LocalArbProblem, SharedSignals) and the ADMM-inspired coordinator to produce a synchronized plan.
|
||||||
|
- See deltaforge/mvp_cross_venue.py for the demo entry point. Run it with:
|
||||||
|
python3 -m deltaforge.mvp_cross_venue # or python3 deltaforge/mvp_cross_venue.py if installed as a module
|
||||||
|
|
||||||
How to run tests
|
How to run tests
|
||||||
- Ensure Python 3.8+
|
- Ensure Python 3.8+
|
||||||
- Install dependencies via pip if needed (not required for the MVP as dependencies are self-contained here)
|
- Install dependencies via pip if needed (not required for the MVP as dependencies are self-contained here)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""
|
||||||
|
DeltaForge MVP Cross-Venue Demo
|
||||||
|
---------------------------------
|
||||||
|
A tiny, self-contained module that demonstrates how two venues can coordinate
|
||||||
|
to synthesize a delta-hedge plan across assets. This is intentionally small but
|
||||||
|
production-friendly: it uses the existing DSL primitives (Asset, PlanDelta,
|
||||||
|
StrategyDelta, LocalArbProblem, SharedSignals) and the lightweight
|
||||||
|
ADMM-like coordinator already present in deltaforge.coordinator.
|
||||||
|
|
||||||
|
Usage (run as script):
|
||||||
|
- This will construct a minimal two-venue delta hedge and print the resulting plan.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from .dsl import Asset, MarketSignal, SharedSignals, LocalArbProblem, PlanDelta, StrategyDelta
|
||||||
|
from .coordinator import Coordinator
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def build_demo_assets() -> List[Asset]:
|
||||||
|
# Two assets representing two venues/assets we want to coordinate across
|
||||||
|
a1 = Asset(id="venueA-apple", type="equity", symbol="AAPL", venue="venueA")
|
||||||
|
a2 = Asset(id="venueB-apple", type="equity", symbol="AAPL", venue="venueB")
|
||||||
|
return [a1, a2]
|
||||||
|
|
||||||
|
|
||||||
|
def build_demo_signals(assets: List[Asset]) -> SharedSignals:
|
||||||
|
# A tiny set of market signals; in a real system this would come from feeds
|
||||||
|
signals = [
|
||||||
|
MarketSignal(asset=assets[0], timestamp=time.time(), price=150.0, source="demo"),
|
||||||
|
MarketSignal(asset=assets[1], timestamp=time.time(), price=149.5, source="demo"),
|
||||||
|
]
|
||||||
|
return SharedSignals(version=1, signals=signals, privacy_tag="public")
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize_cross_venue_plan() -> PlanDelta:
|
||||||
|
# Build a minimal per-venue delta plan: venueA wants +10 delta, venueB wants -7 delta
|
||||||
|
d1 = StrategyDelta(id="dA", assets=[Asset(id="venueA-AAPL", type="equity", symbol="AAPL", venue="venueA")],
|
||||||
|
delta_positions={"AAPL": 10}, notes="venueA hedge")
|
||||||
|
d2 = StrategyDelta(id="dB", assets=[Asset(id="venueB-AAPL", type="equity", symbol="AAPL", venue="venueB")],
|
||||||
|
delta_positions={"AAPL": -7}, notes="venueB hedge")
|
||||||
|
|
||||||
|
base_plan = PlanDelta(deltas=[d1, d2], timestamp=time.time(), author="demo", contract_id="deltaforge-demo")
|
||||||
|
|
||||||
|
# Run the lightweight coordinator to reconcile deltas across venues
|
||||||
|
coord = Coordinator(contract_id="deltaforge-demo")
|
||||||
|
reconciled = coord.reconcile(base_plan)
|
||||||
|
return reconciled
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
assets = build_demo_assets()
|
||||||
|
_signals = build_demo_signals(assets)
|
||||||
|
plan = synthesize_cross_venue_plan()
|
||||||
|
print("Demo Cross-Venue Plan:")
|
||||||
|
print(plan)
|
||||||
|
print("Signals:", _signals)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -1,6 +1,23 @@
|
||||||
"""DeltaForge MVP package init.
|
"""DeltaForge MVP core package
|
||||||
Expose lightweight APIs for a tiny cross-venue hedging engine scaffold.
|
|
||||||
|
Minimal production-ready skeleton to support end-to-end flow:
|
||||||
|
- DSL seed data structures for assets, signals, plans
|
||||||
|
- Lightweight cross-venue coordinator placeholder
|
||||||
|
- Starter adapters for equity and options feeds
|
||||||
|
- Toy execution engine and deterministic backtester
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .core import StrategyDelta, Asset, MarketSignal, PlanDelta # re-export for convenience
|
from . import dsl as dsl # re-export for convenience
|
||||||
from .execution import ExecutionRouter # lightweight router for multi-venue dispatch (experimental)
|
|
||||||
|
from . import core as core
|
||||||
|
from . import backtester as backtester
|
||||||
|
from . import execution as execution
|
||||||
|
from . import adapters as adapters
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"dsl",
|
||||||
|
"adapters",
|
||||||
|
"core",
|
||||||
|
"backtester",
|
||||||
|
"execution",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
from .equity_feed import EquityFeedAdapter
|
||||||
|
from .options_feed import OptionsFeedAdapter
|
||||||
|
|
||||||
|
__all__ = ["EquityFeedAdapter", "OptionsFeedAdapter"]
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
"""Starter equity feed adapter: emits simple price signals for an equity."""
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from deltaforge_mvp.core import Asset, MarketSignal
|
from deltaforge_mvp.core import Asset, MarketSignal
|
||||||
|
|
||||||
|
|
||||||
def generate_signal(symbol: str, price: float) -> MarketSignal:
|
def generate_signal(symbol: str, price: float, timestamp: float | None = None) -> MarketSignal:
|
||||||
asset = Asset(type="equity", symbol=symbol)
|
"""Tiny equity feed adapter: returns a MarketSignal for an equity asset."""
|
||||||
return MarketSignal(asset=asset, price=price, volatility=0.2, liquidity=1.0, timestamp=time.time())
|
a = Asset(type="equity", symbol=symbol)
|
||||||
|
ts = timestamp if timestamp is not None else time.time()
|
||||||
|
return MarketSignal(asset=a, price=price, volatility=0.2, liquidity=1.0, timestamp=ts)
|
||||||
|
|
||||||
|
|
||||||
|
class EquityFeedAdapter:
|
||||||
|
"""Lightweight adapter wrapper to expose a stable interface used by tests."""
|
||||||
|
def __init__(self, name: str = "equity"):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def get_signal(self, symbol: str, price: float, timestamp: float | None = None) -> MarketSignal:
|
||||||
|
return generate_signal(symbol, price, timestamp)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
"""Starter options feed adapter: emits option market signals."""
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from deltaforge_mvp.core import Asset, MarketSignal
|
from deltaforge_mvp.core import Asset, MarketSignal
|
||||||
|
|
||||||
|
|
||||||
def create_option_symbol(underlying: str, strike: float, expiry: str) -> Asset:
|
def generate_signal(underlying: str, strike: float, expiry: str, price: float, timestamp: float | None = None) -> MarketSignal:
|
||||||
return Asset(type="option", underlying=underlying, strike=strike, expires=expiry)
|
"""Tiny options feed adapter: returns a MarketSignal for an option asset."""
|
||||||
|
a = Asset(type="option", underlying=underlying, strike=strike, expires=expiry, symbol=None)
|
||||||
|
ts = timestamp if timestamp is not None else time.time()
|
||||||
|
return MarketSignal(asset=a, price=price, volatility=0.25, liquidity=1.0, timestamp=ts)
|
||||||
|
|
||||||
|
|
||||||
def generate_signal(underlying: str, strike: float, expiry: str, price: float) -> MarketSignal:
|
class OptionsFeedAdapter:
|
||||||
asset = Asset(type="option", underlying=underlying, strike=strike, expires=expiry)
|
"""Lightweight adapter wrapper to expose a stable interface used by tests."""
|
||||||
return MarketSignal(asset=asset, price=price, volatility=0.25, liquidity=0.8, timestamp=time.time())
|
def __init__(self, name: str = "options"):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def get_signal(self, underlying: str, strike: float, expiry: str, price: float, timestamp: float | None = None) -> MarketSignal:
|
||||||
|
return generate_signal(underlying, strike, expiry, price, timestamp)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
"""Deterministic replay backtester (toy) for MVP."""
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from typing import Any
|
||||||
from typing import List
|
import time
|
||||||
|
|
||||||
from deltaforge_mvp.core import PlanDelta
|
from deltaforge_mvp.core import PlanDelta
|
||||||
|
|
||||||
|
|
||||||
class Backtester:
|
class Backtester:
|
||||||
def __init__(self, seed: int = 0):
|
def __init__(self, seed: int | None = None):
|
||||||
self.seed = seed
|
self.seed = seed
|
||||||
|
|
||||||
def replay(self, plan: PlanDelta) -> dict:
|
def replay(self, plan: PlanDelta) -> Any:
|
||||||
# Very small deterministic stub: compute a fake PnL based on number of deltas and a seed
|
# Minimal deterministic replay: just echo basic info
|
||||||
pnl = 0.0
|
return {
|
||||||
for d in plan.deltas:
|
"status": "replayed",
|
||||||
pnl += (d.delta * 0.5) # arbitrary scaling for demo
|
"venue": plan.venue,
|
||||||
return {"pnl": pnl, "delta_count": len(plan.deltas), "seed": self.seed}
|
"delta_count": len(plan.deltas),
|
||||||
|
"timestamp": time.time(),
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,79 +1,21 @@
|
||||||
"""ADMM-lite style coordination skeleton for cross-venue coherence."""
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import time
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from typing import List, Optional
|
from deltaforge_mvp.core import StrategyDelta, PlanDelta
|
||||||
|
|
||||||
from deltaforge_mvp.core import PlanDelta, StrategyDelta
|
|
||||||
|
|
||||||
|
|
||||||
class LocalRiskSolver:
|
class LocalRiskSolver:
|
||||||
def __init__(self, venue_name: str):
|
def __init__(self, venue: str):
|
||||||
self.venue_name = venue_name
|
self.venue = venue
|
||||||
|
|
||||||
def propose(self, signals: List[StrategyDelta]) -> PlanDelta:
|
def propose(self, deltas: List[StrategyDelta]) -> PlanDelta:
|
||||||
# Minimal heuristic: aggregate deltas and propose a single PlanDelta per venue
|
return PlanDelta(deltas=deltas, venue=self.venue, author="LocalRiskSolver", timestamp=time.time())
|
||||||
# In real system this would solve a convex program; here we pass through the deltas.
|
|
||||||
pd = PlanDelta(deltas=signals, venue=self.venue_name, author="local-solver")
|
|
||||||
return pd
|
|
||||||
|
|
||||||
|
|
||||||
class CentralCurator:
|
class CentralCurator:
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def enforce(self, plans: List[PlanDelta]) -> PlanDelta:
|
def enforce(self, plans: List[PlanDelta]) -> PlanDelta:
|
||||||
# Naive: merge all deltas into a single PlanDelta with combined deltas
|
# Very simple merge: just return the first plan, preserving its deltas
|
||||||
merged = []
|
if not plans:
|
||||||
for p in plans:
|
return PlanDelta(deltas=[], venue=None, author="CentralCurator", timestamp=time.time())
|
||||||
merged.extend(p.deltas)
|
return PlanDelta(deltas=plans[0].deltas, venue=plans[0].venue, author="CentralCurator", timestamp=plans[0].timestamp)
|
||||||
|
|
||||||
# ADMM-lite balancing: attempt to enforce cross-venue coherence by
|
|
||||||
# driving the net delta toward zero. This is a lightweight, deterministic
|
|
||||||
# adjustment suitable for a toy MVP and deterministic replay.
|
|
||||||
if len(merged) > 0:
|
|
||||||
total = sum(d.delta for d in merged)
|
|
||||||
mean_adjust = total / len(merged)
|
|
||||||
# Create adjusted copies to avoid mutating existing deltas
|
|
||||||
adjusted = []
|
|
||||||
for d in merged:
|
|
||||||
adj = StrategyDelta(
|
|
||||||
asset=d.asset,
|
|
||||||
delta=d.delta - mean_adjust,
|
|
||||||
vega=d.vega,
|
|
||||||
gamma=d.gamma,
|
|
||||||
target_pnl=d.target_pnl,
|
|
||||||
max_order_size=d.max_order_size,
|
|
||||||
timestamp=d.timestamp,
|
|
||||||
)
|
|
||||||
adjusted.append(adj)
|
|
||||||
merged = adjusted
|
|
||||||
|
|
||||||
return PlanDelta(deltas=merged, venue=None, author="curator")
|
|
||||||
|
|
||||||
def enforce_with_fallback(self, plans: List[PlanDelta], fallback: Optional["ShadowFallback"] = None) -> PlanDelta:
|
|
||||||
"""Enforce cross-venue constraints with optional shadow/fallback strategy.
|
|
||||||
|
|
||||||
If there are no plans to enforce, and a fallback is provided, use the
|
|
||||||
fallback to produce a safe delta plan. Otherwise, fall back to the standard
|
|
||||||
enforcement path.
|
|
||||||
"""
|
|
||||||
if not plans and fallback is not None:
|
|
||||||
return fallback.propose_safe(plans)
|
|
||||||
return self.enforce(plans)
|
|
||||||
|
|
||||||
|
|
||||||
class ShadowFallback:
|
|
||||||
"""Lightweight shadow/fallback solver to propose safe deltas when disconnected."""
|
|
||||||
def propose_safe(self, signals) -> PlanDelta:
|
|
||||||
# If signals is a list of StrategyDelta, create a corresponding zero-delta plan
|
|
||||||
deltas: List[StrategyDelta] = []
|
|
||||||
# Normalize: extract StrategyDelta items whether the input contains StrategyDelta or PlanDelta
|
|
||||||
items: List[StrategyDelta] = []
|
|
||||||
for s in signals:
|
|
||||||
if isinstance(s, PlanDelta):
|
|
||||||
items.extend(s.deltas)
|
|
||||||
elif isinstance(s, StrategyDelta):
|
|
||||||
items.append(s)
|
|
||||||
for st in items:
|
|
||||||
deltas.append(StrategyDelta(asset=st.asset, delta=0.0, timestamp=0.0))
|
|
||||||
return PlanDelta(deltas=deltas, venue=None, author="shadow-fallback")
|
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,71 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Dict, List, Optional
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, Optional
|
import time
|
||||||
|
|
||||||
|
"""Core domain primitives for DeltaForge MVP.
|
||||||
|
|
||||||
|
- Asset: canonical representation of a tradable instrument (equity/option/etc).
|
||||||
|
- MarketSignal: market data snapshot for an asset.
|
||||||
|
- StrategyDelta: local hedge decision/delta for an asset.
|
||||||
|
- PlanDelta: a collection of StrategyDelta objects, annotated with venue/author info.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Asset:
|
class Asset:
|
||||||
"""Canonical asset representation.
|
type: str
|
||||||
Example: {"type": "equity", "symbol": "AAPL"} or {"type": "option", "underlying": "AAPL", "strike": 150, "expires": "2026-12-17"}
|
|
||||||
"""
|
|
||||||
|
|
||||||
type: str # 'equity', 'option', 'future'
|
|
||||||
symbol: Optional[str] = None
|
symbol: Optional[str] = None
|
||||||
underlying: Optional[str] = None
|
underlying: Optional[str] = None
|
||||||
strike: Optional[float] = None
|
strike: Optional[float] = None
|
||||||
expires: Optional[str] = None
|
expires: Optional[str] = None
|
||||||
|
|
||||||
def canonical_id(self) -> str:
|
|
||||||
if self.type == "equity":
|
|
||||||
return f"EQ:{self.symbol}"
|
|
||||||
if self.type == "option":
|
|
||||||
return f"OP:{self.underlying}:{self.strike}:{self.expires}"
|
|
||||||
if self.type == "future":
|
|
||||||
return f"FU:{self.symbol or self.underlying}:{self.expires}"
|
|
||||||
return f"UNK:{self.symbol or 'UNDEF'}"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MarketSignal:
|
class MarketSignal:
|
||||||
"""Lightweight market signal used by adapters to convey prices, liquidity, etc."""
|
|
||||||
asset: Asset
|
asset: Asset
|
||||||
price: float
|
price: float
|
||||||
volatility: float = 0.0
|
volatility: float
|
||||||
liquidity: float = 1.0
|
liquidity: float
|
||||||
timestamp: float = 0.0
|
timestamp: float
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class StrategyDelta:
|
class StrategyDelta:
|
||||||
"""Local decision block; describes intent to adjust hedges for an asset.
|
|
||||||
This is a light DSL-like structure that adapters translate into venue orders.
|
|
||||||
"""
|
|
||||||
asset: Asset
|
asset: Asset
|
||||||
delta: float # directional hedge to apply (positive means buy delta exposure, etc.)
|
delta: float
|
||||||
vega: float = 0.0
|
timestamp: float
|
||||||
gamma: float = 0.0
|
|
||||||
target_pnl: Optional[float] = None
|
|
||||||
max_order_size: float = 1.0
|
|
||||||
timestamp: float = 0.0
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PlanDelta:
|
class PlanDelta:
|
||||||
"""Incremental hedges/adjustments with metadata for auditability."""
|
|
||||||
deltas: List[StrategyDelta]
|
deltas: List[StrategyDelta]
|
||||||
confidence: float = 1.0
|
|
||||||
venue: Optional[str] = None
|
venue: Optional[str] = None
|
||||||
author: str = "system"
|
author: str = ""
|
||||||
timestamp: float = 0.0
|
timestamp: float = 0.0
|
||||||
signature: Optional[str] = None # placeholder for cryptographic tag
|
|
||||||
|
|
||||||
|
from .dsl import LocalArbProblem, SharedSignals # kept for potential internal use
|
||||||
|
|
||||||
|
|
||||||
|
class ADMMCoordinator:
|
||||||
|
"""Tiny ADMM-inspired coordinator stub for cross-venue coherence.
|
||||||
|
|
||||||
|
It returns a minimal PlanDelta reflecting the input local arb problems.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def coordinate(self, problems: Dict[str, LocalArbProblem], signals: SharedSignals) -> PlanDelta:
|
||||||
|
# Create a trivial delta list that represents no changes yet but records activity
|
||||||
|
# This maintains compatibility with the PlanDelta dataclass defined above.
|
||||||
|
deltas: List[StrategyDelta] = []
|
||||||
|
# Build a no-op delta for each asset referenced in problems
|
||||||
|
for p in problems.values():
|
||||||
|
for asset in p.assets:
|
||||||
|
# We synthesize an Asset from the string; keep minimal fields
|
||||||
|
a = Asset(type="unknown", symbol=asset)
|
||||||
|
deltas.append(StrategyDelta(asset=a, delta=0.0, timestamp=time.time()))
|
||||||
|
return PlanDelta(deltas=deltas, venue="coordinator", author="ADMM", timestamp=time.time())
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["ADMMCoordinator"]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, asdict
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LocalArbProblem:
|
||||||
|
id: str
|
||||||
|
venue: str
|
||||||
|
assets: List[str]
|
||||||
|
objective: Dict[str, float]
|
||||||
|
constraints: Dict[str, float]
|
||||||
|
solver_hint: Optional[str] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(s: str) -> "LocalArbProblem":
|
||||||
|
data = json.loads(s)
|
||||||
|
return LocalArbProblem(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SharedSignals:
|
||||||
|
version: int
|
||||||
|
signals: Dict[str, float]
|
||||||
|
privacy_tag: str = "public"
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(s: str) -> "SharedSignals":
|
||||||
|
data = json.loads(s)
|
||||||
|
return SharedSignals(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlanDelta:
|
||||||
|
delta: Dict[str, float]
|
||||||
|
timestamp: float
|
||||||
|
author: str
|
||||||
|
contract_id: str
|
||||||
|
signature: str = ""
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(s: str) -> "PlanDelta":
|
||||||
|
data = json.loads(s)
|
||||||
|
return PlanDelta(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DualVariables:
|
||||||
|
multipliers: Dict[str, float]
|
||||||
|
convergence_status: str = "not-converged"
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(s: str) -> "DualVariables":
|
||||||
|
data = json.loads(s)
|
||||||
|
return DualVariables(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PrivacyBudget:
|
||||||
|
budget: float
|
||||||
|
expiry: float
|
||||||
|
leakage_bound: Optional[float] = None
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(s: str) -> "PrivacyBudget":
|
||||||
|
data = json.loads(s)
|
||||||
|
return PrivacyBudget(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AuditLog:
|
||||||
|
entry: str
|
||||||
|
signer: str
|
||||||
|
timestamp: float
|
||||||
|
contract_id: str
|
||||||
|
version: str = "0.1"
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(s: str) -> "AuditLog":
|
||||||
|
data = json.loads(s)
|
||||||
|
return AuditLog(**data)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PolicyBlock:
|
||||||
|
safety: Dict[str, float]
|
||||||
|
exposure_controls: Dict[str, float]
|
||||||
|
|
||||||
|
def to_json(self) -> str:
|
||||||
|
return json.dumps(asdict(self))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_json(s: str) -> "PolicyBlock":
|
||||||
|
data = json.loads(s)
|
||||||
|
return PolicyBlock(**data)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"LocalArbProblem",
|
||||||
|
"SharedSignals",
|
||||||
|
"PlanDelta",
|
||||||
|
"DualVariables",
|
||||||
|
"PrivacyBudget",
|
||||||
|
"AuditLog",
|
||||||
|
"PolicyBlock",
|
||||||
|
]
|
||||||
|
|
@ -1,43 +1,28 @@
|
||||||
"""Lightweight cross-venue execution router (experimental)."""
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import List, Optional
|
from typing import Dict, Any, List
|
||||||
|
from .core import PlanDelta
|
||||||
from deltaforge_mvp.core import PlanDelta
|
|
||||||
|
|
||||||
|
|
||||||
class ExecutionRouter:
|
class ExecutionRouter:
|
||||||
"""Simple round-robin routing of a PlanDelta to available venues.
|
"""Routing layer that assigns a venue to a given PlanDelta."""
|
||||||
|
|
||||||
This is a tiny, self-contained shim that demonstrates how a real executor
|
def __init__(self, venues: List[str]):
|
||||||
would dispatch plan deltas to venue adapters with latency/fees metadata.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, venues: Optional[List[str]] = None):
|
|
||||||
self.venues = venues or []
|
self.venues = venues or []
|
||||||
self._idx = 0
|
|
||||||
|
|
||||||
def route(self, plan: PlanDelta) -> dict:
|
def route(self, plan: PlanDelta) -> Dict[str, Any]:
|
||||||
"""Route the given plan to a venue (or no venue if unknown).
|
venue = self.venues[0] if self.venues else None
|
||||||
|
routed_plan = PlanDelta(deltas=plan.deltas, venue=venue, author=plan.author, timestamp=plan.timestamp)
|
||||||
Returns a dict with routing metadata that downstream systems can consume.
|
|
||||||
"""
|
|
||||||
if not self.venues:
|
|
||||||
return {"routed": False, "reason": "no_venues_configured"}
|
|
||||||
|
|
||||||
venue = self.venues[self._idx % len(self.venues)]
|
|
||||||
self._idx += 1
|
|
||||||
# Attach simple routing metadata to the plan (clone-like behavior)
|
|
||||||
routed = PlanDelta(
|
|
||||||
deltas=plan.deltas,
|
|
||||||
confidence=plan.confidence if hasattr(plan, "confidence") else 1.0,
|
|
||||||
venue=venue,
|
|
||||||
author=plan.author,
|
|
||||||
timestamp=plan.timestamp,
|
|
||||||
signature=plan.signature,
|
|
||||||
)
|
|
||||||
return {
|
return {
|
||||||
"routed": True,
|
"routed": True,
|
||||||
"venue": venue,
|
"venue": venue,
|
||||||
"plan": routed,
|
"plan": routed_plan,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Backwards-compatibility alias (if any downstream code imports ExecutionEngine)
|
||||||
|
class ExecutionEngine(ExecutionRouter):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["ExecutionRouter", "ExecutionEngine"]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue