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

This commit is contained in:
agent-58ba63c88b4c9625 2026-04-20 16:13:15 +02:00
parent c519cc58c6
commit 68c5a8454d
4 changed files with 103 additions and 4 deletions

View File

@ -17,6 +17,7 @@ __all__ = [
"OptionsFeedAdapter",
"ExecutionEngine",
"Backtester",
"RealTimeEngine",
]
from .dsl import Asset, MarketSignal, StrategyDelta, PlanDelta
@ -25,3 +26,4 @@ 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

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from datetime import datetime, timedelta
from typing import Iterator
from ..dsl import MarketSignal
from ..dsl import MarketSignal, Asset
class EquityFeedAdapter:
@ -17,10 +17,18 @@ class EquityFeedAdapter:
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))
yield MarketSignal(asset_symbol=sym, venue=venue, price=float(price), timestamp=t + timedelta(seconds=i))
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

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from datetime import datetime, timedelta
from typing import Iterator
from ..dsl import MarketSignal
from ..dsl import MarketSignal, Asset
class OptionsFeedAdapter:
@ -23,4 +23,7 @@ class OptionsFeedAdapter:
for a in self.assets:
symbol = a.get("symbol")
price = 5.0 * (1 + i * 0.01)
yield MarketSignal(asset_symbol=symbol, venue=venue, price=price, timestamp=t + timedelta(seconds=i))
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})

86
deltaforge/rt_engine.py Normal file
View File

@ -0,0 +1,86 @@
from __future__ import annotations
from typing import List
from .adapters.equity_feed import EquityFeedAdapter
from .adapters.options_feed import OptionsFeedAdapter
from .core import Curator
from .coordinator import Coordinator
from .execution import ExecutionEngine
from .backtester import Backtester
from .dsl import MarketSignal, PlanDelta, StrategyDelta, Asset
class RealTimeEngine:
"""Minimal real-time cross-asset engine (MVP).
Streams signals from equity and options adapters, synthesizes a plan via the
curator, coordinates cross-venue coherence, and executes the plan. Also runs a
deterministic replay for testing purposes.
"""
def __init__(self,
equity_symbols: List[str] | None = None,
option_assets: List[dict] | None = None,
venues: List[str] | None = None,
sample_window: int = 4):
self.equity_symbols = equity_symbols or ["AAPL", "MSFT"]
self.option_assets = option_assets or [{"symbol": "AAPL", "type": "call"}, {"symbol": "MSFT", "type": "put"}]
self.venues = venues or ["VENUE-A", "VENUE-B"]
self.sample_window = sample_window
# Adapters
self.equity_adapter = EquityFeedAdapter(symbols=self.equity_symbols, venues=self.venues)
self.option_adapter = OptionsFeedAdapter(assets=self.option_assets, venues=self.venues)
# Lightweight components
self.curator = Curator()
self.coordinator = Coordinator()
self.engine = ExecutionEngine()
self.bt = Backtester(initial_cash=100000.0)
def _collect_signals(self) -> List[MarketSignal]:
signals: List[MarketSignal] = []
eq_iter = self.equity_adapter.stream_signals()
opt_iter = self.option_adapter.stream_signals()
for _ in range(self.sample_window):
try:
s = next(eq_iter)
signals.append(s)
except StopIteration:
pass
try:
s = next(opt_iter)
signals.append(s)
except StopIteration:
pass
return signals
def run_cycle(self) -> dict:
"""Run a single deterministic cycle: collect signals, synthesize and coordinate a plan, and execute."""
signals = self._collect_signals()
# Build simple objectives from signals
objectives: List[StrategyDelta] = []
for idx, sig in enumerate(signals):
asset = sig.asset
hedges = 0.5 # simple heuristic placeholder
objectives.append(StrategyDelta(id=f"rt-{idx}", assets=[asset], hedge_ratio=hedges, target_pnl=0.0, constraints=[]))
# Curate a plan from signals (optional in MVP) we keep this lightweight
plan_curated: PlanDelta = self.curator.synthesize_plan(signals, objectives)
# Cross-venue coordination step (ADMM-lite)
plan = self.coordinator.coordinate(signals, author="rt-engine")
# Execute plan (routing and PnL proxy)
exec_result = self.engine.execute(plan)
# Backtest replay for deterministic evaluation
replay = self.bt.run(plan)
return {
"plan": plan,
"execution": exec_result,
"replay": replay,
}