87 lines
3.2 KiB
Python
87 lines
3.2 KiB
Python
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,
|
||
}
|