Team Ai
Modelpublic

ParallelLLC/algorithmic_trading

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
27likes32downloads
execution_agent.py305 linesDownload Raw Back to agentic_ai_system
1import logging2import time3from typing import Dict, Any, Optional4from .agent_base import Agent5 6class ExecutionAgent(Agent):7    def __init__(self, config: Dict[str, Any]):8        super().__init__(config)9        self.broker_api = config['execution']['broker_api']10        self.order_size = config['execution']['order_size']11        self.execution_delay = config.get('execution', {}).get('delay_ms', 100)12        self.success_rate = config.get('execution', {}).get('success_rate', 0.95)13        14        # Initialize Alpaca broker if configured15        self.alpaca_broker = None16        if self.broker_api in ['alpaca_paper', 'alpaca_live']:17            try:18                from .alpaca_broker import AlpacaBroker19                self.alpaca_broker = AlpacaBroker(config)20                self.logger.info(f"Alpaca broker initialized for {self.broker_api}")21            except Exception as e:22                self.logger.error(f"Failed to initialize Alpaca broker: {e}")23                self.broker_api = 'paper'  # Fallback to simulation24        25        self.logger.info(f"Execution agent initialized with {self.broker_api} broker")26    27    def act(self, signal: Dict[str, Any]) -> Dict[str, Any]:28        """29        Execute trading signal by sending order to broker.30        31        Args:32            signal: Dictionary containing trading signal33            34        Returns:35            Dictionary containing execution result36        """37        try:38            self.logger.info(f"Processing execution signal: {signal['action']} {signal['quantity']} {signal['symbol']}")39            40            # Validate signal41            if not self._validate_signal(signal):42                self.logger.warning("Invalid signal received, skipping execution")43                return self._generate_execution_result(signal, success=False, error="Invalid signal")44            45            # Execute order based on broker type46            if self.broker_api in ['alpaca_paper', 'alpaca_live'] and self.alpaca_broker:47                execution_result = self._execute_alpaca_order(signal)48            else:49                execution_result = self._execute_simulated_order(signal)50            51            # Log execution result52            self.log_action(execution_result)53            54            return execution_result55            56        except Exception as e:57            self.log_error(e, "Error in order execution")58            return self._generate_execution_result(signal, success=False, error=str(e))59    60    def _execute_alpaca_order(self, signal: Dict[str, Any]) -> Dict[str, Any]:61        """Execute order using Alpaca broker"""62        try:63            if signal['action'] == 'hold':64                return self._generate_execution_result(signal, success=True, error=None)65            66            # Place market order with Alpaca67            result = self.alpaca_broker.place_market_order(68                symbol=signal['symbol'],69                quantity=signal['quantity'],70                side=signal['action']71            )72            73            # Convert Alpaca result to our format74            execution_result = {75                'order_id': result.get('order_id'),76                'status': result.get('status', 'unknown'),77                'action': signal['action'],78                'symbol': signal['symbol'],79                'quantity': signal['quantity'],80                'price': result.get('filled_avg_price', signal.get('price', 0)),81                'execution_time': time.time(),82                'commission': self._calculate_commission(signal),83                'total_value': result.get('filled_avg_price', 0) * signal['quantity'] if result.get('filled_avg_price') else 0,84                'success': result.get('success', False),85                'error': result.get('error')86            }87            88            if execution_result['success']:89                self.logger.info(f"Alpaca order executed successfully: {execution_result['order_id']}")90            else:91                self.logger.error(f"Alpaca order failed: {execution_result['error']}")92            93            return execution_result94            95        except Exception as e:96            self.log_error(e, "Error in Alpaca order execution")97            return self._generate_execution_result(signal, success=False, error=str(e))98    99    def _execute_simulated_order(self, signal: Dict[str, Any]) -> Dict[str, Any]:100        """Execute order with broker simulation"""101        try:102            # Simulate execution delay103            time.sleep(self.execution_delay / 1000.0)104            105            # Simulate execution success/failure106            import random107            success = random.random() < self.success_rate108            109            if signal['action'] == 'hold':110                success = True  # Hold actions always succeed111            112            if success:113                return self._simulate_successful_execution(signal)114            else:115                return self._simulate_failed_execution(signal)116                117        except Exception as e:118            self.log_error(e, "Error in order execution simulation")119            return self._generate_execution_result(signal, success=False, error=str(e))120    121    def get_account_info(self) -> Dict[str, Any]:122        """Get account information"""123        if self.alpaca_broker:124            return self.alpaca_broker.get_account_info()125        else:126            # Return simulated account info127            return {128                'account_id': 'SIM_ACCOUNT',129                'status': 'ACTIVE',130                'buying_power': 100000.0,131                'cash': 100000.0,132                'portfolio_value': 100000.0,133                'equity': 100000.0,134                'trading_blocked': False135            }136    137    def get_positions(self) -> list:138        """Get current positions"""139        if self.alpaca_broker:140            return self.alpaca_broker.get_positions()141        else:142            # Return simulated positions143            return []144    145    def is_market_open(self) -> bool:146        """Check if market is open"""147        if self.alpaca_broker:148            return self.alpaca_broker.is_market_open()149        else:150            # Assume market is always open for simulation151            return True152 153    def _validate_signal(self, signal: Dict[str, Any]) -> bool:154        """Validate trading signal"""155        try:156            required_fields = ['action', 'symbol', 'quantity']157            158            # Check required fields159            for field in required_fields:160                if field not in signal:161                    self.logger.error(f"Missing required field: {field}")162                    return False163            164            # Validate action165            if signal['action'] not in ['buy', 'sell', 'hold']:166                self.logger.error(f"Invalid action: {signal['action']}")167                return False168            169            # Validate quantity170            if signal['quantity'] <= 0 and signal['action'] != 'hold':171                self.logger.error(f"Invalid quantity: {signal['quantity']}")172                return False173            174            # Validate symbol175            if not signal['symbol'] or not isinstance(signal['symbol'], str):176                self.logger.error(f"Invalid symbol: {signal['symbol']}")177                return False178            179            return True180            181        except Exception as e:182            self.log_error(e, "Error validating signal")183            return False184    185    def _simulate_successful_execution(self, signal: Dict[str, Any]) -> Dict[str, Any]:186        """Simulate successful order execution"""187        try:188            # Generate execution details189            execution_price = signal.get('price', 0)190            if execution_price == 0:191                # Simulate price slippage192                import random193                slippage = random.uniform(-0.001, 0.001)  # ±0.1% slippage194                execution_price = signal.get('price', 100) * (1 + slippage)195            196            execution_time = time.time()197            198            # Calculate fees (simplified)199            commission = self._calculate_commission(signal)200            201            result = {202                'order_id': self._generate_order_id(),203                'status': 'filled',204                'action': signal['action'],205                'symbol': signal['symbol'],206                'quantity': signal['quantity'],207                'price': round(execution_price, 4),208                'execution_time': execution_time,209                'commission': commission,210                'total_value': round(signal['quantity'] * execution_price, 2),211                'success': True,212                'error': None213            }214            215            self.logger.info(f"Order executed successfully: {result['order_id']} - "216                           f"{result['action']} {result['quantity']} {result['symbol']} @ {result['price']}")217            218            return result219            220        except Exception as e:221            self.log_error(e, "Error in successful execution simulation")222            return self._generate_execution_result(signal, success=False, error=str(e))223    224    def _simulate_failed_execution(self, signal: Dict[str, Any]) -> Dict[str, Any]:225        """Simulate failed order execution"""226        error_reasons = [227            "Insufficient funds",228            "Market closed",229            "Invalid order",230            "Network timeout",231            "Broker error"232        ]233        234        import random235        error_reason = random.choice(error_reasons)236        237        result = self._generate_execution_result(signal, success=False, error=error_reason)238        239        self.logger.warning(f"Order execution failed: {error_reason}")240        241        return result242    243    def _generate_execution_result(self, signal: Dict[str, Any], success: bool, error: Optional[str] = None) -> Dict[str, Any]:244        """Generate execution result"""245        return {246            'order_id': self._generate_order_id() if success else None,247            'status': 'filled' if success else 'rejected',248            'action': signal.get('action', 'unknown'),249            'symbol': signal.get('symbol', 'unknown'),250            'quantity': signal.get('quantity', 0),251            'price': signal.get('price', 0) if success else 0,  # Price is 0 for failed executions252            'execution_time': time.time(),253            'commission': 0,254            'total_value': 0,255            'success': success,256            'error': error257        }258    259    def _calculate_commission(self, signal: Dict[str, Any]) -> float:260        """Calculate commission for the order"""261        try:262            # Simple commission calculation263            base_commission = 1.0  # $1 base commission264            per_share_commission = 0.01  # $0.01 per share265            266            if signal['action'] == 'hold':267                return 0.0268            269            commission = base_commission + (signal['quantity'] * per_share_commission)270            return round(commission, 2)271            272        except Exception as e:273            self.log_error(e, "Error calculating commission")274            return 0.0275    276    def _execute_order(self, signal: Dict[str, Any]) -> Dict[str, Any]:277        """278        Execute a trading order (private method for testing)279        280        Args:281            signal: Trading signal282            283        Returns:284            Execution result285        """286        return self.act(signal)287    288    def _generate_order_id(self) -> str:289        """Generate unique order ID"""290        import uuid291        return f"ORD_{uuid.uuid4().hex[:8].upper()}"292    293    def get_execution_statistics(self) -> Dict[str, Any]:294        """Get execution statistics"""295        # This would typically track real execution statistics296        # For now, return placeholder data297        return {298            'total_orders': 0,299            'successful_orders': 0,300            'failed_orders': 0,301            'success_rate': 0.0,302            'average_execution_time': 0.0,303            'total_commission': 0.0304        }305