initial: import DOLPHIN baseline 2026-04-21 from dolphinng5_predict working tree
Includes core prod + GREEN/BLUE subsystems: - prod/ (BLUE harness, configs, scripts, docs) - nautilus_dolphin/ (GREEN Nautilus-native impl + dvae/ preserved) - adaptive_exit/ (AEM engine + models/bucket_assignments.pkl) - Observability/ (EsoF advisor, TUI, dashboards) - external_factors/ (EsoF producer) - mc_forewarning_qlabs_fork/ (MC regime/envelope) Excludes runtime caches, logs, backups, and reproducible artifacts per .gitignore.
This commit is contained in:
0
prod/clean_arch/core/__init__.py
Executable file
0
prod/clean_arch/core/__init__.py
Executable file
185
prod/clean_arch/core/trading_engine.py
Executable file
185
prod/clean_arch/core/trading_engine.py
Executable file
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
CORE: TradingEngine
|
||||
===================
|
||||
Pure business logic - no external dependencies.
|
||||
|
||||
Clean Architecture:
|
||||
- Depends only on PORTS (interfaces)
|
||||
- No knowledge of Hazelcast, Binance, etc.
|
||||
- Testable in isolation
|
||||
- Ready for Rust kernel migration
|
||||
"""
|
||||
|
||||
import logging
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Any
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
# Import only PORTS, not adapters
|
||||
import sys
|
||||
from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
from ports.data_feed import DataFeedPort, MarketSnapshot, ACBUpdate
|
||||
|
||||
logger = logging.getLogger("TradingEngine")
|
||||
|
||||
|
||||
@dataclass
|
||||
class Position:
|
||||
"""Current position state."""
|
||||
symbol: str
|
||||
side: str # 'LONG' or 'SHORT'
|
||||
size: float
|
||||
entry_price: float
|
||||
entry_time: datetime
|
||||
unrealized_pnl: float = 0.0
|
||||
|
||||
|
||||
@dataclass
|
||||
class TradingState:
|
||||
"""Complete trading state (serializable)."""
|
||||
capital: float
|
||||
positions: Dict[str, Position] = field(default_factory=dict)
|
||||
trades_today: int = 0
|
||||
daily_pnl: float = 0.0
|
||||
last_update: Optional[datetime] = None
|
||||
|
||||
def total_exposure(self) -> float:
|
||||
"""Calculate total position exposure."""
|
||||
return sum(abs(p.size * p.entry_price) for p in self.positions.values())
|
||||
|
||||
|
||||
class TradingEngine:
|
||||
"""
|
||||
CORE: Pure trading logic.
|
||||
|
||||
No external dependencies - works with any DataFeedPort implementation.
|
||||
Can be unit tested with mock feeds.
|
||||
Ready for Rust rewrite (state machine is simple).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data_feed: DataFeedPort,
|
||||
config: Dict[str, Any]
|
||||
):
|
||||
self.feed = data_feed
|
||||
self.config = config
|
||||
|
||||
# State
|
||||
self.state = TradingState(
|
||||
capital=config.get('initial_capital', 25000.0)
|
||||
)
|
||||
self.running = False
|
||||
|
||||
# Strategy params
|
||||
self.max_leverage = config.get('max_leverage', 5.0)
|
||||
self.capital_fraction = config.get('capital_fraction', 0.20)
|
||||
self.min_irp = config.get('min_irp_alignment', 0.45)
|
||||
self.vel_div_threshold = config.get('vel_div_threshold', -0.02)
|
||||
|
||||
# ACB state
|
||||
self.acb_boost = 1.0
|
||||
self.acb_beta = 0.5
|
||||
self.posture = 'APEX'
|
||||
|
||||
logger.info("TradingEngine initialized")
|
||||
logger.info(f" Capital: ${self.state.capital:,.2f}")
|
||||
logger.info(f" Max Leverage: {self.max_leverage}x")
|
||||
logger.info(f" Capital Fraction: {self.capital_fraction:.0%}")
|
||||
|
||||
async def start(self):
|
||||
"""Start the trading engine."""
|
||||
logger.info("=" * 60)
|
||||
logger.info("🐬 TRADING ENGINE STARTING")
|
||||
logger.info("=" * 60)
|
||||
|
||||
# Connect to data feed
|
||||
if not await self.feed.connect():
|
||||
raise RuntimeError("Failed to connect to data feed")
|
||||
|
||||
self.running = True
|
||||
|
||||
# Subscribe to snapshot stream
|
||||
await self.feed.subscribe_snapshots(self._on_snapshot)
|
||||
|
||||
logger.info("[✓] Engine running - waiting for data...")
|
||||
|
||||
# Main loop
|
||||
while self.running:
|
||||
await self._process_cycle()
|
||||
await asyncio.sleep(5) # 5s cycle
|
||||
|
||||
async def stop(self):
|
||||
"""Stop cleanly."""
|
||||
self.running = False
|
||||
await self.feed.disconnect()
|
||||
logger.info("=" * 60)
|
||||
logger.info("🙏 TRADING ENGINE STOPPED")
|
||||
logger.info(f" Final Capital: ${self.state.capital:,.2f}")
|
||||
logger.info(f" Daily PnL: ${self.state.daily_pnl:,.2f}")
|
||||
logger.info("=" * 60)
|
||||
|
||||
async def _process_cycle(self):
|
||||
"""Main processing cycle."""
|
||||
try:
|
||||
# Update ACB
|
||||
acb = await self.feed.get_acb_update()
|
||||
if acb:
|
||||
self._update_acb(acb)
|
||||
|
||||
# Health check
|
||||
if not self.feed.health_check():
|
||||
logger.warning("[!] Data feed unhealthy")
|
||||
return
|
||||
|
||||
# Log heartbeat
|
||||
now = datetime.utcnow()
|
||||
if not self.state.last_update or (now - self.state.last_update).seconds >= 60:
|
||||
self._log_status()
|
||||
self.state.last_update = now
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Cycle error: {e}")
|
||||
|
||||
def _on_snapshot(self, snapshot: MarketSnapshot):
|
||||
"""
|
||||
Callback for new market snapshot.
|
||||
Receives PRICE + EIGENVALUES (synced).
|
||||
"""
|
||||
if not snapshot.is_valid():
|
||||
return
|
||||
|
||||
# Log heartbeat
|
||||
if snapshot.scan_number and snapshot.scan_number % 12 == 0:
|
||||
logger.info(f"[TICK] {snapshot.symbol} @ ${snapshot.price:,.2f} "
|
||||
f"(scan #{snapshot.scan_number})")
|
||||
|
||||
self._evaluate_signal(snapshot)
|
||||
|
||||
def _evaluate_signal(self, snapshot: MarketSnapshot):
|
||||
"""Evaluate trading signal - all data synced."""
|
||||
# Trading logic here
|
||||
pass
|
||||
|
||||
def _update_acb(self, acb: ACBUpdate):
|
||||
"""Update ACB parameters."""
|
||||
self.acb_boost = acb.boost
|
||||
self.acb_beta = acb.beta
|
||||
self.posture = acb.posture
|
||||
|
||||
def _log_status(self):
|
||||
"""Log current status."""
|
||||
latency = self.feed.get_latency_ms()
|
||||
exposure = self.state.total_exposure()
|
||||
|
||||
logger.info("=" * 40)
|
||||
logger.info(f"STATUS: Capital=${self.state.capital:,.2f}")
|
||||
logger.info(f" Daily PnL=${self.state.daily_pnl:,.2f}")
|
||||
logger.info(f" Exposure=${exposure:,.2f}")
|
||||
logger.info(f" Positions={len(self.state.positions)}")
|
||||
logger.info(f" Latency={latency:.1f}ms")
|
||||
logger.info(f" ACB Boost={self.acb_boost:.2f}")
|
||||
logger.info("=" * 40)
|
||||
Reference in New Issue
Block a user