build(agent): new-agents-4#58ba63 iteration
This commit is contained in:
parent
bd54a28a2f
commit
120ddefebc
|
|
@ -0,0 +1,12 @@
|
|||
# DeltaForge Skeleton Addendum
|
||||
|
||||
- This repository now includes a production-oriented MVP skeleton for DeltaForge:
|
||||
- Core DSL: Asset, MarketSignal, StrategyDelta, PlanDelta, SharedSignals, DualVariables (deltaforge_skeleton/dsl.py)
|
||||
- Lightweight Curator (ADMM-like) in deltaforge_skeleton/curator.py
|
||||
- Two starter adapters: deltaforge_skeleton/adapters/equity_feed.py and deltaforge_skeleton/adapters/options_feed.py
|
||||
- Minimal ExecutionEngine (deltaforge_skeleton/execution.py) and Backtester (deltaforge_skeleton/backtester.py)
|
||||
- Packaging metadata (pyproject.toml) and a basic test harness (deltaforge_skeleton/tests/test_basic.py)
|
||||
- How to run tests: ./test.sh
|
||||
- Packaging test: python3 -m build
|
||||
- Tests are unittest-based and run via python3 -m unittest discover -s deltaforge_skeleton/tests
|
||||
- Ready-to-publish guard: READY_TO_PUBLISH exists at repo root when MVP is production-ready
|
||||
20
README.md
20
README.md
|
|
@ -1,12 +1,16 @@
|
|||
# DeltaForge Skeleton
|
||||
|
||||
A production-friendly Python package skeleton for DeltaForge MVP, enabling a canonical DSL surface,
|
||||
two-venue adapters, a lightweight ADMM-like coordinator, and deterministic replay-backed backtests.
|
||||
A production-oriented MVP scaffold for a real-time cross-venue delta-hedge engine.
|
||||
|
||||
- Core DSL: Asset, MarketSignal, StrategyDelta, PlanDelta
|
||||
- Adapters: EquityFeed, OptionsFeed (two starter adapters)
|
||||
- Curator: simple cross-venue planning
|
||||
- ExecutionEngine and Backtester: deterministic routing and replay
|
||||
- GoC registry scaffolding for future interoperability
|
||||
- Core DSL: Asset, MarketSignal, StrategyDelta, PlanDelta, SharedSignals, DualVariables
|
||||
- Lightweight curator (ADMM-like) for cross-venue coherence
|
||||
- Two starter adapters: equity_feed and options_feed
|
||||
- Minimal ExecutionEngine to route actions across venues
|
||||
- Toy Backtester with deterministic replay
|
||||
- Packaging metadata and test harness to verify packaging and end-to-end flow
|
||||
|
||||
This repository is designed to be extended toward a production MVP with robust contracts and governance.
|
||||
How to run tests
|
||||
- python3 -m build
|
||||
- pytest (if pytest is installed) or python3 -m unittest discover -s deltaforge_skeleton/tests
|
||||
|
||||
This skeleton is designed to be extended into a full MVP within the DeltaForge ecosystem.
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
"""DeltaForge Skeletal MVP
|
||||
"""DeltaForge Skeleton package
|
||||
|
||||
A minimal, production-friendly Python package skeleton that implements a
|
||||
canonical DSL surface and a toy but deterministic cross-venue workflow for
|
||||
testing and integration with adapters.
|
||||
A minimal, production-oriented MVP scaffold for a cross-venue delta-hedge engine.
|
||||
"""
|
||||
|
||||
from .core import Asset, MarketSignal, StrategyDelta, PlanDelta
|
||||
from .adapters.equity_feed import EquityFeedAdapter
|
||||
from .adapters.options_feed import OptionsFeedAdapter
|
||||
from .dsl import Asset, MarketSignal, StrategyDelta, PlanDelta, SharedSignals, DualVariables
|
||||
from .curator import Curator
|
||||
from .goc_registry import GoCRegistry
|
||||
from .adapters import equity_feed as equity_feed
|
||||
from .adapters import options_feed as options_feed
|
||||
from .execution import ExecutionEngine
|
||||
from .backtester import Backtester
|
||||
|
||||
|
|
@ -18,10 +15,11 @@ __all__ = [
|
|||
"MarketSignal",
|
||||
"StrategyDelta",
|
||||
"PlanDelta",
|
||||
"EquityFeedAdapter",
|
||||
"OptionsFeedAdapter",
|
||||
"SharedSignals",
|
||||
"DualVariables",
|
||||
"Curator",
|
||||
"equity_feed",
|
||||
"options_feed",
|
||||
"ExecutionEngine",
|
||||
"Backtester",
|
||||
"GoCRegistry",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
"""Adapters package"""
|
||||
from . import equity_feed
|
||||
from . import options_feed
|
||||
|
||||
from .equity_feed import EquityFeedAdapter
|
||||
from .options_feed import OptionsFeedAdapter
|
||||
|
||||
__all__ = ["EquityFeedAdapter", "OptionsFeedAdapter"]
|
||||
__all__ = ["equity_feed", "options_feed"]
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from deltaforge_skeleton.core import MarketSignal, Asset
|
||||
from typing import List
|
||||
from ..dsl import Asset, MarketSignal
|
||||
|
||||
|
||||
@dataclass
|
||||
class EquityFeedAdapter:
|
||||
name: str = "EquityFeed"
|
||||
|
||||
def synthesize(self, symbol: str, price: float, timestamp: float) -> MarketSignal:
|
||||
asset = Asset(symbol=symbol, asset_type="equity")
|
||||
return MarketSignal(asset=asset, price=price, timestamp=timestamp, liquidity=1.0)
|
||||
def generate_equity_signals(symbols: List[str], price_dict: dict) -> List[MarketSignal]:
|
||||
signals = []
|
||||
import time
|
||||
ts = time.time()
|
||||
for s in symbols:
|
||||
asset = Asset(symbol=s, asset_type="equity")
|
||||
price = price_dict.get(s, 100.0)
|
||||
signals.append(MarketSignal(asset=asset, price=price, timestamp=ts))
|
||||
return signals
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from deltaforge_skeleton.core import MarketSignal, Asset
|
||||
from typing import List
|
||||
from ..dsl import Asset, MarketSignal
|
||||
|
||||
|
||||
@dataclass
|
||||
class OptionsFeedAdapter:
|
||||
name: str = "OptionsFeed"
|
||||
|
||||
def synthesize(self, symbol: str, price: float, timestamp: float) -> MarketSignal:
|
||||
def generate_options_signals(symbol: str, underlying_price: float) -> MarketSignal:
|
||||
asset = Asset(symbol=symbol, asset_type="option")
|
||||
return MarketSignal(asset=asset, price=price, timestamp=timestamp, liquidity=0.8)
|
||||
# Very naive option price proxy for this MVP: static premium around 5% of underlying
|
||||
price = max(1.0, underlying_price * 0.05)
|
||||
import time
|
||||
return MarketSignal(asset=asset, price=price, timestamp=time.time())
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from deltaforge_skeleton.core import PlanDelta
|
||||
from typing import List
|
||||
from .dsl import PlanDelta, StrategyDelta
|
||||
|
||||
|
||||
class Backtester:
|
||||
def replay(self, plan: PlanDelta) -> str:
|
||||
# Deterministic replay: encode steps into a single string log
|
||||
if not plan.steps:
|
||||
return "empty plan"
|
||||
return " | ".join(plan.steps)
|
||||
"""Toy deterministic replay engine for end-to-end validation."""
|
||||
|
||||
def __init__(self, seed: int = 0):
|
||||
self.seed = seed
|
||||
|
||||
def replay(self, plan: PlanDelta) -> float:
|
||||
# Very naive replay: sum absolute deltas to produce a pseudo-PnL
|
||||
pnl = sum(abs(a.delta) for a in plan.actions)
|
||||
# Incorporate timestamp entropy for determinism in tests
|
||||
pnl += (plan.timestamp or 0.0) * 1e-6
|
||||
return float(pnl)
|
||||
|
|
|
|||
|
|
@ -1,41 +1,28 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
from deltaforge_skeleton.core import MarketSignal, PlanDelta, StrategyDelta, Asset
|
||||
|
||||
# Optional cross-venue coordination (ADMM-lite) import. The coordination
|
||||
# module is kept minimal to avoid imposing heavy dependencies on the MVP.
|
||||
try:
|
||||
from deltaforge_skeleton.coordination import ADMMCoordinator
|
||||
except Exception:
|
||||
ADMMCoordinator = None # type: ignore
|
||||
from .dsl import PlanDelta, SharedSignals, DualVariables
|
||||
|
||||
|
||||
class Curator:
|
||||
"""A lightweight ADMM-like coordinator for cross-venue coherence.
|
||||
|
||||
This is a minimal stub that validates a PlanDelta against a simple
|
||||
constraint: total delta across venues must sum to zero (delta-neutral).
|
||||
In a real system, this would implement distributed optimization across venues.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.audit = []
|
||||
|
||||
def synthesize(self, signals: List[MarketSignal]) -> PlanDelta:
|
||||
# Naive delta-synthesis: for each asset, create a delta hedge with 1x price weight
|
||||
steps: List[str] = []
|
||||
for s in signals:
|
||||
asset = s.asset
|
||||
# simple heuristic: hedge ratio proportional to liquidity and inverse of price
|
||||
hedge_ratio = max(0.0, min(1.0, 1.0 * (s.liquidity / max(1.0, s.price))))
|
||||
obj = StrategyDelta(asset=asset, hedge_ratio=hedge_ratio, target_pnl=0.0, constraints=[])
|
||||
steps.append(f"HEDGE {asset.symbol} with ratio {hedge_ratio:.3f}")
|
||||
plan = PlanDelta(steps=steps, timestamp=0.0, provenance="deltaforge-skeleton-curation")
|
||||
self.audit.append("synthesized plan from signals")
|
||||
|
||||
# Optional cross-venue coordination: if a real ADMM coordinator is available,
|
||||
# attempt to coordinate across a default two-venue setup. This keeps the MVP
|
||||
# lightweight while enabling future multi-venue coherence checks.
|
||||
if ADMMCoordinator is not None:
|
||||
try:
|
||||
coordinator = ADMMCoordinator()
|
||||
plan = coordinator.coordinate(plan, venues=["VenueA", "VenueB"])
|
||||
except Exception:
|
||||
# Silently ignore coordination issues to preserve existing behavior
|
||||
pass
|
||||
|
||||
return plan
|
||||
def enforce(self, local_plan: PlanDelta, shared: SharedSignals, duals: DualVariables) -> PlanDelta:
|
||||
# Simple validation: if sum of deltas != 0, attempt to rebalance by offsetting last action
|
||||
total = sum(a.delta for a in local_plan.actions)
|
||||
if total != 0 and local_plan.actions:
|
||||
# Adjust the last action to ensure delta-neutrality as a placeholder behavior
|
||||
last = local_plan.actions[-1]
|
||||
offset = -total
|
||||
last.delta += offset
|
||||
# Attach a simple safety tag for auditability in this MVP
|
||||
local_plan.safety_tags = local_plan.safety_tags or []
|
||||
local_plan.safety_tags.append("delta-neutralized-by-curator")
|
||||
return local_plan
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
from __future__ import annotations
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class Asset:
|
||||
symbol: str
|
||||
asset_type: str # e.g., 'equity' or 'option'
|
||||
|
||||
def __post_init__(self):
|
||||
if not self.symbol:
|
||||
raise ValueError("Asset.symbol must be non-empty")
|
||||
if self.asset_type not in {"equity", "option", "future"}:
|
||||
raise ValueError("asset_type must be 'equity', 'option', or 'future'")
|
||||
|
||||
|
||||
@dataclass
|
||||
class MarketSignal:
|
||||
asset: Asset
|
||||
price: float
|
||||
timestamp: float
|
||||
|
||||
|
||||
@dataclass
|
||||
class StrategyDelta:
|
||||
# A high-level delta that would be applied to a local problem
|
||||
asset: Asset
|
||||
delta: float
|
||||
theta: Optional[float] = None # optional auxiliary metric
|
||||
|
||||
|
||||
@dataclass
|
||||
class SharedSignals:
|
||||
prices: Dict[str, float] # symbol -> price
|
||||
greeks: Dict[str, Dict[str, float]] # symbol -> greek-name -> value
|
||||
liquidity: Dict[str, float] # symbol -> liquidity proxy
|
||||
|
||||
|
||||
@dataclass
|
||||
class DualVariables:
|
||||
coupling_multipliers: Dict[str, float] # venue_id -> multiplier
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlanDelta:
|
||||
# A delta plan that can be applied to move a local problem toward feasibility
|
||||
actions: List[StrategyDelta] = field(default_factory=list)
|
||||
timestamp: float = 0.0
|
||||
safety_tags: List[str] = field(default_factory=list)
|
||||
|
|
@ -1,19 +1,20 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
from deltaforge_skeleton.core import PlanDelta
|
||||
|
||||
from .dsl import PlanDelta, StrategyDelta
|
||||
|
||||
|
||||
class ExecutionEngine:
|
||||
"""Minimal, latency-aware router that would dispatch actions to venues."""
|
||||
|
||||
def __init__(self):
|
||||
self.log: List[str] = []
|
||||
pass
|
||||
|
||||
def route(self, plan: PlanDelta) -> List[str]:
|
||||
# Very small mock routing: just annotate steps with a venue tag
|
||||
routed = []
|
||||
for i, step in enumerate(plan.steps or []):
|
||||
venue = "VenueA" if i % 2 == 0 else "VenueB"
|
||||
action = f"{step} -> {venue}"
|
||||
routed.append(action)
|
||||
self.log.append("routed plan across venues")
|
||||
return routed
|
||||
# In this MVP, we simply return venue labels for actions as a stub
|
||||
venue_actions = []
|
||||
for a in plan.actions:
|
||||
venue_actions.append(f"venue-A/{a.asset.symbol}:{a.delta}")
|
||||
if not venue_actions:
|
||||
venue_actions.append("no-actions")
|
||||
return venue_actions
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
import unittest
|
||||
|
||||
from deltaforge_skeleton.dsl import Asset, PlanDelta, StrategyDelta
|
||||
from deltaforge_skeleton.curator import Curator
|
||||
from deltaforge_skeleton.adapters.equity_feed import generate_equity_signals
|
||||
from deltaforge_skeleton.adapters.options_feed import generate_options_signals
|
||||
from deltaforge_skeleton.execution import ExecutionEngine
|
||||
from deltaforge_skeleton.backtester import Backtester
|
||||
|
||||
|
||||
class TestDeltaForgeSkeleton(unittest.TestCase):
|
||||
def test_end_to_end_scenario(self):
|
||||
# Simple two-asset delta hedge: AAPL and MSFT across two venues (conceptual)
|
||||
aapl = Asset(symbol="AAPL", asset_type="equity")
|
||||
msft = Asset(symbol="MSFT", asset_type="equity")
|
||||
|
||||
# Create two simple plan deltas to simulate actions on each venue
|
||||
plan = PlanDelta(actions=[StrategyDelta(asset=aapl, delta=1.0), StrategyDelta(asset=msft, delta=-1.0)], timestamp=123.0)
|
||||
|
||||
curator = Curator()
|
||||
# Shared signals and dual vars are placeholders for this MVP
|
||||
class Dummy:
|
||||
pass
|
||||
shared = Dummy()
|
||||
duals = Dummy()
|
||||
curated = curator.enforce(plan, shared, duals)
|
||||
|
||||
eng = ExecutionEngine()
|
||||
routed = eng.route(curated)
|
||||
|
||||
bt = Backtester()
|
||||
pnl = bt.replay(curated)
|
||||
|
||||
self.assertTrue(len(routed) >= 1)
|
||||
self.assertIsInstance(pnl, float)
|
||||
|
||||
|
||||
def main():
|
||||
unittest.main()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,16 +1,14 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=61", "wheel"]
|
||||
requires = ["setuptools>=42", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "deltaforge-skeleton"
|
||||
version = "0.1.0"
|
||||
description = "DeltaForge MVP skeleton: cross-venue synthesis engine with two assets and two venues."
|
||||
description = "DeltaForge MVP skeleton: core DSL, curator, adapters, execution, and backtester for cross-venue hedges (2 assets, 2 venues)"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
license = {text = "MIT"}
|
||||
authors = [ { name = "OpenCode DeltaForge", email = "dev@example.com" } ]
|
||||
dependencies = []
|
||||
license = "MIT"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["deltaforge_skeleton"]
|
||||
|
|
|
|||
36
test.sh
36
test.sh
|
|
@ -3,36 +3,10 @@ set -euo pipefail
|
|||
|
||||
echo "Running DeltaForge Skeleton tests..."
|
||||
|
||||
# Ensure Python is available
|
||||
python3 -V
|
||||
pip3 -V
|
||||
# 1) Build the package to verify packaging metadata and directory structure
|
||||
python3 -m build
|
||||
|
||||
# Build the package to verify packaging metadata compiles
|
||||
python3 -m build || { echo "Build failed"; exit 1; }
|
||||
# 2) Run unit tests (standard library unittest)
|
||||
python3 -m unittest discover -s deltaforge_skeleton/tests
|
||||
|
||||
echo "Running a minimal deterministic flow..."
|
||||
python3 - << 'PY'
|
||||
from deltaforge_skeleton.core import Asset, MarketSignal
|
||||
from deltaforge_skeleton.adapters.equity_feed import EquityFeedAdapter
|
||||
from deltaforge_skeleton.curator import Curator
|
||||
from deltaforge_skeleton.execution import ExecutionEngine
|
||||
from deltaforge_skeleton.backtester import Backtester
|
||||
|
||||
apple = Asset(symbol='AAPL')
|
||||
sig = MarketSignal(asset=apple, price=150.0, timestamp=0.0, liquidity=1.0)
|
||||
|
||||
curator = Curator()
|
||||
plan = curator.synthesize([sig])
|
||||
|
||||
engine = ExecutionEngine()
|
||||
routes = engine.route(plan)
|
||||
|
||||
bt = Backtester()
|
||||
replay = bt.replay(plan)
|
||||
|
||||
print("PLAN:", plan)
|
||||
print("ROUTES:", routes)
|
||||
print("REPLAY:", replay)
|
||||
PY
|
||||
|
||||
echo "All good. Ready to publish once READY_TO_PUBLISH is created."
|
||||
echo "All tests passed."
|
||||
|
|
|
|||
Loading…
Reference in New Issue