ParallelLLC/algorithmic_trading
2732
1import logging2import time3import pandas as pd4from typing import Dict, Any, Optional5from .data_ingestion import load_data, validate_data6from .strategy_agent import StrategyAgent7from .execution_agent import ExecutionAgent8 9logger = logging.getLogger(__name__)10 11def run(config: Dict[str, Any]) -> Dict[str, Any]:12 """13 Main orchestration function that coordinates the trading workflow.14 15 Args:16 config: Configuration dictionary17 18 Returns:19 Dictionary containing execution results and statistics20 """21 start_time = time.time()22 logger.info("Starting trading system orchestration")23 24 try:25 # Initialize workflow results26 workflow_result = {27 'success': False,28 'data_loaded': False,29 'signal_generated': False,30 'order_executed': False,31 'execution_result': None,32 'errors': [],33 'execution_time': 034 }35 36 # Step 1: Load market data37 logger.info("Step 1: Loading market data")38 data = load_data(config)39 40 if data is not None and not data.empty:41 workflow_result['data_loaded'] = True42 logger.info(f"Successfully loaded {len(data)} data points")43 44 # Validate data quality45 if validate_data(data):46 logger.info("Data validation passed")47 else:48 logger.warning("Data validation failed, but continuing with workflow")49 else:50 logger.error("Failed to load market data")51 workflow_result['errors'].append("Failed to load market data")52 return workflow_result53 54 # Step 2: Generate trading signal55 logger.info("Step 2: Generating trading signal")56 strategy_agent = StrategyAgent(config)57 signal = strategy_agent.act(data)58 59 if signal and signal.get('action') != 'hold':60 workflow_result['signal_generated'] = True61 logger.info(f"Generated signal: {signal['action']} {signal['quantity']} {signal['symbol']}")62 else:63 logger.info("No actionable signal generated (hold)")64 workflow_result['signal_generated'] = True # Hold is still a valid signal65 66 # Step 3: Execute order67 logger.info("Step 3: Executing order")68 execution_agent = ExecutionAgent(config)69 execution_result = execution_agent.act(signal)70 71 if execution_result['success']:72 workflow_result['order_executed'] = True73 workflow_result['execution_result'] = execution_result74 logger.info("Order executed successfully")75 else:76 logger.error(f"Order execution failed: {execution_result.get('error', 'Unknown error')}")77 workflow_result['errors'].append(f"Order execution failed: {execution_result.get('error')}")78 79 # Calculate execution time80 workflow_result['execution_time'] = time.time() - start_time81 workflow_result['success'] = workflow_result['data_loaded'] and workflow_result['signal_generated']82 83 logger.info(f"Trading workflow completed in {workflow_result['execution_time']:.2f} seconds")84 85 return workflow_result86 87 except Exception as e:88 logger.error(f"Error in trading workflow: {e}", exc_info=True)89 workflow_result = {90 'success': False,91 'data_loaded': False,92 'signal_generated': False,93 'order_executed': False,94 'execution_result': None,95 'errors': [str(e)],96 'execution_time': time.time() - start_time97 }98 return workflow_result99 100def run_backtest(config: Dict[str, Any], start_date: str = '2024-01-01', end_date: str = '2024-12-31') -> Dict[str, Any]:101 """102 Run backtesting simulation over historical data.103 104 Args:105 config: Configuration dictionary106 start_date: Start date for backtest107 end_date: End date for backtest108 109 Returns:110 Dictionary containing backtest results111 """112 logger.info(f"Starting backtest from {start_date} to {end_date}")113 114 try:115 # Load historical data116 data = load_data(config)117 118 if data is None or data.empty:119 logger.error("No data available for backtest")120 return {'success': False, 'error': 'No data available'}121 122 # Filter data for backtest period123 data['timestamp'] = pd.to_datetime(data['timestamp'])124 mask = (data['timestamp'] >= start_date) & (data['timestamp'] <= end_date)125 backtest_data = data.loc[mask]126 127 if backtest_data.empty:128 logger.error("No data available for specified backtest period")129 return {'success': False, 'error': 'No data for backtest period'}130 131 logger.info(f"Running backtest on {len(backtest_data)} data points")132 133 # Initialize agents134 strategy_agent = StrategyAgent(config)135 execution_agent = ExecutionAgent(config)136 137 # Track backtest results138 trades = []139 portfolio_value = config['trading']['capital']140 positions = {}141 142 # Run simulation143 for i in range(len(backtest_data)):144 current_data = backtest_data.iloc[:i+1]145 146 if len(current_data) < 50: # Need minimum data for indicators147 continue148 149 # Generate signal150 signal = strategy_agent.act(current_data)151 152 # Execute if not hold153 if signal['action'] != 'hold':154 execution_result = execution_agent.act(signal)155 trades.append({156 'timestamp': current_data.index[-1],157 'signal': signal,158 'execution': execution_result159 })160 161 # Update portfolio (simplified)162 if execution_result['success']:163 symbol = signal['symbol']164 if signal['action'] == 'buy':165 positions[symbol] = positions.get(symbol, 0) + signal['quantity']166 portfolio_value -= execution_result['total_value']167 elif signal['action'] == 'sell':168 positions[symbol] = positions.get(symbol, 0) - signal['quantity']169 portfolio_value += execution_result['total_value']170 171 # Calculate final portfolio value172 final_value = portfolio_value173 for symbol, quantity in positions.items():174 if quantity > 0:175 final_price = backtest_data['close'].iloc[-1]176 final_value += quantity * final_price177 178 # Calculate performance metrics179 total_return = (final_value - config['trading']['capital']) / config['trading']['capital']180 181 backtest_results = {182 'success': True,183 'start_date': start_date,184 'end_date': end_date,185 'initial_capital': config['trading']['capital'],186 'final_value': final_value,187 'total_return': total_return,188 'total_trades': len(trades),189 'trades': trades,190 'positions': positions191 }192 193 logger.info(f"Backtest completed: {total_return:.2%} return over {len(trades)} trades")194 return backtest_results195 196 except Exception as e:197 logger.error(f"Error in backtest: {e}", exc_info=True)198 return {'success': False, 'error': str(e)}199 200def run_live_trading(config: Dict[str, Any], duration_minutes: int = 60) -> Dict[str, Any]:201 """202 Run live trading simulation for a specified duration.203 204 Args:205 config: Configuration dictionary206 duration_minutes: Duration to run live trading in minutes207 208 Returns:209 Dictionary containing live trading results210 """211 logger.info(f"Starting live trading simulation for {duration_minutes} minutes")212 213 try:214 import time215 from datetime import datetime, timedelta216 217 end_time = datetime.now() + timedelta(minutes=duration_minutes)218 trades = []219 220 while datetime.now() < end_time:221 # Run single trading cycle222 result = run(config)223 224 if result['order_executed'] and result['execution_result']['success']:225 trades.append(result['execution_result'])226 227 # Wait before next cycle228 time.sleep(60) # Wait 1 minute between cycles229 230 live_results = {231 'success': True,232 'duration_minutes': duration_minutes,233 'total_trades': len(trades),234 'trades': trades,235 'start_time': datetime.now() - timedelta(minutes=duration_minutes),236 'end_time': datetime.now()237 }238 239 logger.info(f"Live trading completed: {len(trades)} trades executed")240 return live_results241 242 except Exception as e:243 logger.error(f"Error in live trading: {e}", exc_info=True)244 return {'success': False, 'error': str(e)}245 