ParallelLLC/algorithmic_trading
2732
1"""2Streamlit UI for Algorithmic Trading System3 4A comprehensive web interface for:5- Real-time market data visualization6- Trading strategy configuration7- FinRL model training and evaluation8- Portfolio management9- Risk monitoring10"""11 12import streamlit as st13import plotly.graph_objects as go14import plotly.express as px15import pandas as pd16import numpy as np17import yaml18import os19import sys20from datetime import datetime, timedelta21from typing import Dict, Any, Optional22import asyncio23import threading24import time25 26# Add project root to path27sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))28 29# Import with error handling for deployment30try:31 from agentic_ai_system.main import load_config32 from agentic_ai_system.data_ingestion import load_data, validate_data, add_technical_indicators33 from agentic_ai_system.finrl_agent import FinRLAgent, FinRLConfig34 from agentic_ai_system.alpaca_broker import AlpacaBroker35 from agentic_ai_system.orchestrator import run_backtest, run_live_trading36 DEPLOYMENT_MODE = False37except ImportError as e:38 st.warning(f"โ ๏ธ Some modules not available in deployment mode: {e}")39 DEPLOYMENT_MODE = True40 41 # Mock functions for deployment42 def load_config(config_file):43 return {44 'trading': {'symbol': 'AAPL', 'capital': 100000, 'timeframe': '1d'},45 'execution': {'broker_api': 'alpaca_paper'},46 'finrl': {'algorithm': 'PPO'},47 'risk': {'max_drawdown': 0.1}48 }49 50 def load_data(config):51 # Generate sample data for deployment52 dates = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')53 np.random.seed(42)54 prices = 150 + np.cumsum(np.random.randn(len(dates)) * 0.5)55 56 data = pd.DataFrame({57 'timestamp': dates,58 'open': prices * 0.99,59 'high': prices * 1.02,60 'low': prices * 0.98,61 'close': prices,62 'volume': np.random.randint(1000000, 5000000, len(dates))63 })64 return data65 66 def add_technical_indicators(data):67 data['sma_20'] = data['close'].rolling(window=20).mean()68 data['sma_50'] = data['close'].rolling(window=50).mean()69 return data70 71 class FinRLAgent:72 def __init__(self, config):73 self.config = config74 75 def train(self, data, config, total_timesteps, use_real_broker=False):76 return {'success': True, 'message': 'Training completed (demo mode)'}77 78 class FinRLConfig:79 def __init__(self, **kwargs):80 for key, value in kwargs.items():81 setattr(self, key, value)82 83 class AlpacaBroker:84 def __init__(self, config):85 self.config = config86 87 def get_account_info(self):88 return {89 'portfolio_value': 100000,90 'equity': 102500,91 'cash': 50000,92 'buying_power': 5000093 }94 95 def get_positions(self):96 return []97 98 def run_backtest(config, data):99 return {100 'success': True,101 'total_return': 0.025,102 'sharpe_ratio': 1.2,103 'max_drawdown': 0.05,104 'total_trades': 15105 }106 107 def run_live_trading(config, data):108 return {'success': True, 'message': 'Live trading started (demo mode)'}109 110# Page configuration111st.set_page_config(112 page_title="Algorithmic Trading System",113 page_icon="๐",114 layout="wide",115 initial_sidebar_state="expanded"116)117 118# Custom CSS for better styling119st.markdown("""120<style>121 .main-header {122 font-size: 2.5rem;123 font-weight: bold;124 color: #1f77b4;125 text-align: center;126 margin-bottom: 2rem;127 }128 .metric-card {129 background-color: #f0f2f6;130 padding: 1rem;131 border-radius: 0.5rem;132 border-left: 4px solid #1f77b4;133 }134 .success-metric {135 border-left-color: #28a745;136 }137 .warning-metric {138 border-left-color: #ffc107;139 }140 .danger-metric {141 border-left-color: #dc3545;142 }143 .sidebar .sidebar-content {144 background-color: #f8f9fa;145 }146</style>147""", unsafe_allow_html=True)148 149class TradingUI:150 def __init__(self):151 self.config = None152 self.data = None153 self.alpaca_broker = None154 self.finrl_agent = None155 self.session_state = st.session_state156 157 # Initialize session state158 if 'trading_active' not in self.session_state:159 self.session_state.trading_active = False160 if 'current_portfolio' not in self.session_state:161 self.session_state.current_portfolio = {}162 if 'trading_history' not in self.session_state:163 self.session_state.trading_history = []164 165 def load_configuration(self):166 """Load and display configuration"""167 st.sidebar.header("โ๏ธ Configuration")168 169 # Config file selector170 config_files = [f for f in os.listdir('.') if f.endswith('.yaml') or f.endswith('.yml')]171 selected_config = st.sidebar.selectbox(172 "Select Configuration File",173 config_files,174 index=0 if 'config.yaml' in config_files else 0175 )176 177 if st.sidebar.button("Load Configuration"):178 try:179 self.config = load_config(selected_config)180 st.sidebar.success(f"โ
Configuration loaded: {selected_config}")181 return True182 except Exception as e:183 st.sidebar.error(f"โ Error loading config: {e}")184 return False185 186 return False187 188 def display_system_status(self):189 """Display system status and metrics"""190 st.header("๐ System Status")191 192 col1, col2, col3, col4 = st.columns(4)193 194 with col1:195 st.metric(196 label="Trading Status",197 value="๐ข Active" if self.session_state.trading_active else "๐ด Inactive",198 delta="Running" if self.session_state.trading_active else "Stopped"199 )200 201 with col2:202 if self.config:203 st.metric(204 label="Capital",205 value=f"${self.config['trading']['capital']:,}",206 delta="Available"207 )208 else:209 st.metric(label="Capital", value="Not Loaded")210 211 with col3:212 if self.alpaca_broker:213 try:214 account_info = self.alpaca_broker.get_account_info()215 if account_info:216 st.metric(217 label="Portfolio Value",218 value=f"${float(account_info['portfolio_value']):,.2f}",219 delta=f"{float(account_info['equity']) - float(account_info['portfolio_value']):,.2f}"220 )221 except:222 st.metric(label="Portfolio Value", value="Not Connected")223 else:224 st.metric(label="Portfolio Value", value="Not Connected")225 226 with col4:227 if self.data is not None:228 st.metric(229 label="Data Points",230 value=f"{len(self.data):,}",231 delta=f"Latest: {self.data['timestamp'].max().strftime('%Y-%m-%d')}"232 )233 else:234 st.metric(label="Data Points", value="Not Loaded")235 236 def data_ingestion_panel(self):237 """Data ingestion and visualization panel"""238 st.header("๐ฅ Data Ingestion")239 240 col1, col2 = st.columns([2, 1])241 242 with col1:243 if self.config:244 if st.button("Load Market Data"):245 with st.spinner("Loading data..."):246 try:247 self.data = load_data(self.config)248 if self.data is not None and not self.data.empty:249 st.success(f"โ
Loaded {len(self.data)} data points")250 251 # Add technical indicators252 self.data = add_technical_indicators(self.data)253 st.info(f"โ
Added technical indicators")254 else:255 st.error("โ Failed to load data")256 except Exception as e:257 st.error(f"โ Error loading data: {e}")258 259 with col2:260 if self.data is not None:261 st.subheader("Data Summary")262 st.write(f"**Symbol:** {self.config['trading']['symbol']}")263 st.write(f"**Timeframe:** {self.config['trading']['timeframe']}")264 st.write(f"**Date Range:** {self.data['timestamp'].min().strftime('%Y-%m-%d')} to {self.data['timestamp'].max().strftime('%Y-%m-%d')}")265 st.write(f"**Price Range:** ${self.data['close'].min():.2f} - ${self.data['close'].max():.2f}")266 267 # Data visualization268 if self.data is not None:269 st.subheader("๐ Market Data Visualization")270 271 # Chart type selector272 chart_type = st.selectbox(273 "Chart Type",274 ["Candlestick", "Line", "OHLC", "Volume"]275 )276 277 if chart_type == "Candlestick":278 fig = go.Figure(data=[go.Candlestick(279 x=self.data['timestamp'],280 open=self.data['open'],281 high=self.data['high'],282 low=self.data['low'],283 close=self.data['close']284 )])285 fig.update_layout(286 title=f"{self.config['trading']['symbol']} Candlestick Chart",287 xaxis_title="Date",288 yaxis_title="Price ($)",289 height=500290 )291 st.plotly_chart(fig, use_container_width=True)292 293 elif chart_type == "Line":294 fig = px.line(self.data, x='timestamp', y='close', 295 title=f"{self.config['trading']['symbol']} Price Chart")296 fig.update_layout(height=500)297 st.plotly_chart(fig, use_container_width=True)298 299 elif chart_type == "Volume":300 fig = go.Figure()301 fig.add_trace(go.Bar(302 x=self.data['timestamp'],303 y=self.data['volume'],304 name='Volume'305 ))306 fig.update_layout(307 title=f"{self.config['trading']['symbol']} Volume Chart",308 xaxis_title="Date",309 yaxis_title="Volume",310 height=500311 )312 st.plotly_chart(fig, use_container_width=True)313 314 def alpaca_integration_panel(self):315 """Alpaca broker integration panel"""316 st.header("๐ฆ Alpaca Integration")317 318 col1, col2 = st.columns([1, 1])319 320 with col1:321 if st.button("Connect to Alpaca"):322 if self.config and self.config['execution']['broker_api'] in ['alpaca_paper', 'alpaca_live']:323 with st.spinner("Connecting to Alpaca..."):324 try:325 self.alpaca_broker = AlpacaBroker(self.config)326 account_info = self.alpaca_broker.get_account_info()327 if account_info:328 st.success("โ
Connected to Alpaca")329 self.session_state.alpaca_connected = True330 else:331 st.error("โ Failed to connect to Alpaca")332 except Exception as e:333 st.error(f"โ Connection error: {e}")334 else:335 st.warning("โ ๏ธ Alpaca not configured in settings")336 337 with col2:338 if st.button("Disconnect from Alpaca"):339 self.alpaca_broker = None340 self.session_state.alpaca_connected = False341 st.success("โ
Disconnected from Alpaca")342 343 # Account information display344 if self.alpaca_broker:345 st.subheader("Account Information")346 347 try:348 account_info = self.alpaca_broker.get_account_info()349 if account_info:350 col1, col2, col3 = st.columns(3)351 352 with col1:353 st.metric(354 label="Buying Power",355 value=f"${float(account_info['buying_power']):,.2f}"356 )357 358 with col2:359 st.metric(360 label="Portfolio Value",361 value=f"${float(account_info['portfolio_value']):,.2f}"362 )363 364 with col3:365 st.metric(366 label="Equity",367 value=f"${float(account_info['equity']):,.2f}"368 )369 370 # Market hours371 market_hours = self.alpaca_broker.get_market_hours()372 if market_hours:373 status_color = "๐ข" if market_hours['is_open'] else "๐ด"374 st.info(f"{status_color} Market Status: {'OPEN' if market_hours['is_open'] else 'CLOSED'}")375 376 if market_hours['next_open']:377 st.write(f"Next Open: {market_hours['next_open']}")378 if market_hours['next_close']:379 st.write(f"Next Close: {market_hours['next_close']}")380 381 # Current positions382 positions = self.alpaca_broker.get_positions()383 if positions:384 st.subheader("Current Positions")385 positions_df = pd.DataFrame(positions)386 st.dataframe(positions_df)387 else:388 st.info("No current positions")389 390 except Exception as e:391 st.error(f"Error fetching account info: {e}")392 393 def finrl_training_panel(self):394 """FinRL model training panel"""395 st.header("๐ง FinRL Model Training")396 397 if not self.data is not None:398 st.warning("โ ๏ธ Please load market data first")399 return400 401 col1, col2 = st.columns([2, 1])402 403 with col1:404 st.subheader("Training Configuration")405 406 # Training parameters407 algorithm = st.selectbox(408 "Algorithm",409 ["PPO", "A2C", "DDPG", "TD3"],410 index=0411 )412 413 learning_rate = st.slider(414 "Learning Rate",415 min_value=0.0001,416 max_value=0.01,417 value=0.0003,418 step=0.0001,419 format="%.4f"420 )421 422 total_timesteps = st.slider(423 "Total Timesteps",424 min_value=1000,425 max_value=1000000,426 value=100000,427 step=1000428 )429 430 batch_size = st.selectbox(431 "Batch Size",432 [32, 64, 128, 256],433 index=1434 )435 436 with col2:437 st.subheader("Training Controls")438 439 if st.button("Start Training", type="primary"):440 if self.data is not None:441 with st.spinner("Training FinRL model..."):442 try:443 # Create FinRL config444 finrl_config = FinRLConfig(445 algorithm=algorithm,446 learning_rate=learning_rate,447 batch_size=batch_size,448 buffer_size=1000000,449 learning_starts=100,450 gamma=0.99,451 tau=0.005,452 train_freq=1,453 gradient_steps=1,454 verbose=1,455 tensorboard_log='logs/finrl_tensorboard'456 )457 458 # Initialize agent459 self.finrl_agent = FinRLAgent(finrl_config)460 461 # Train the agent462 result = self.finrl_agent.train(463 data=self.data,464 config=self.config,465 total_timesteps=total_timesteps,466 use_real_broker=False467 )468 469 if result['success']:470 st.success("โ
Training completed successfully!")471 st.write(f"Model saved: {result['model_path']}")472 self.session_state.model_trained = True473 else:474 st.error("โ Training failed")475 476 except Exception as e:477 st.error(f"โ Training error: {e}")478 479 # Training progress and metrics480 if hasattr(self.session_state, 'model_trained') and self.session_state.model_trained:481 st.subheader("Model Performance")482 483 if st.button("Evaluate Model"):484 if self.finrl_agent:485 with st.spinner("Evaluating model..."):486 try:487 # Use last 100 data points for evaluation488 eval_data = self.data.tail(100)489 prediction_result = self.finrl_agent.predict(490 data=eval_data,491 config=self.config,492 use_real_broker=False493 )494 495 if prediction_result['success']:496 col1, col2, col3 = st.columns(3)497 498 with col1:499 st.metric(500 label="Initial Value",501 value=f"${prediction_result['initial_value']:,.2f}"502 )503 504 with col2:505 st.metric(506 label="Final Value",507 value=f"${prediction_result['final_value']:,.2f}"508 )509 510 with col3:511 return_pct = prediction_result['total_return'] * 100512 st.metric(513 label="Total Return",514 value=f"{return_pct:.2f}%",515 delta=f"{return_pct:.2f}%"516 )517 518 st.write(f"Total Trades: {prediction_result['total_trades']}")519 else:520 st.error("โ Model evaluation failed")521 522 except Exception as e:523 st.error(f"โ Evaluation error: {e}")524 525 def trading_controls_panel(self):526 """Trading controls and execution panel"""527 st.header("๐ฏ Trading Controls")528 529 col1, col2 = st.columns([1, 1])530 531 with col1:532 st.subheader("Backtesting")533 534 if st.button("Run Backtest"):535 if self.data is not None and self.config:536 with st.spinner("Running backtest..."):537 try:538 result = run_backtest(self.config, self.data)539 if result['success']:540 st.success("โ
Backtest completed")541 542 # Display backtest results543 col1, col2, col3 = st.columns(3)544 545 with col1:546 st.metric(547 label="Total Return",548 value=f"{result['total_return']:.2%}"549 )550 551 with col2:552 st.metric(553 label="Sharpe Ratio",554 value=f"{result['sharpe_ratio']:.2f}"555 )556 557 with col3:558 st.metric(559 label="Max Drawdown",560 value=f"{result['max_drawdown']:.2%}"561 )562 563 # Store results in session state564 self.session_state.backtest_results = result565 else:566 st.error("โ Backtest failed")567 568 except Exception as e:569 st.error(f"โ Backtest error: {e}")570 571 with col2:572 st.subheader("Live Trading")573 574 if st.button("Start Live Trading", type="primary"):575 if self.config and self.alpaca_broker:576 self.session_state.trading_active = True577 st.success("โ
Live trading started")578 579 # Start trading in background thread580 def run_trading():581 try:582 run_live_trading(self.config, self.data)583 except Exception as e:584 st.error(f"Trading error: {e}")585 586 trading_thread = threading.Thread(target=run_trading)587 trading_thread.daemon = True588 trading_thread.start()589 else:590 st.warning("โ ๏ธ Please configure Alpaca connection first")591 592 if st.button("Stop Live Trading"):593 self.session_state.trading_active = False594 st.success("โ
Live trading stopped")595 596 def portfolio_monitoring_panel(self):597 """Portfolio monitoring and analytics panel"""598 st.header("๐ Portfolio Monitoring")599 600 if not self.alpaca_broker:601 st.warning("โ ๏ธ Connect to Alpaca to view portfolio")602 return603 604 try:605 # Portfolio overview606 account_info = self.alpaca_broker.get_account_info()607 if account_info:608 col1, col2, col3, col4 = st.columns(4)609 610 with col1:611 st.metric(612 label="Total Value",613 value=f"${float(account_info['portfolio_value']):,.2f}"614 )615 616 with col2:617 st.metric(618 label="Cash",619 value=f"${float(account_info['cash']):,.2f}"620 )621 622 with col3:623 st.metric(624 label="Buying Power",625 value=f"${float(account_info['buying_power']):,.2f}"626 )627 628 with col4:629 equity = float(account_info['equity'])630 portfolio_value = float(account_info['portfolio_value'])631 pnl = equity - portfolio_value632 st.metric(633 label="P&L",634 value=f"${pnl:,.2f}",635 delta=f"{pnl:,.2f}"636 )637 638 # Positions table639 positions = self.alpaca_broker.get_positions()640 if positions:641 st.subheader("Current Positions")642 643 positions_df = pd.DataFrame(positions)644 if not positions_df.empty:645 # Calculate additional metrics646 positions_df['market_value'] = positions_df['quantity'].astype(float) * positions_df['current_price'].astype(float)647 positions_df['unrealized_pl'] = positions_df['unrealized_pl'].astype(float)648 positions_df['unrealized_plpc'] = positions_df['unrealized_plpc'].astype(float)649 650 # Display positions651 st.dataframe(652 positions_df[['symbol', 'quantity', 'current_price', 'market_value', 'unrealized_pl', 'unrealized_plpc']],653 use_container_width=True654 )655 656 # Position chart657 fig = px.pie(658 positions_df, 659 values='market_value', 660 names='symbol',661 title="Portfolio Allocation"662 )663 st.plotly_chart(fig, use_container_width=True)664 else:665 st.info("No positions found")666 else:667 st.info("No current positions")668 669 except Exception as e:670 st.error(f"Error fetching portfolio data: {e}")671 672 def run(self):673 """Main UI application"""674 # Header675 st.markdown('<h1 class="main-header">๐ค Algorithmic Trading System</h1>', unsafe_allow_html=True)676 677 # Load configuration678 if self.load_configuration():679 self.config = load_config('config.yaml')680 681 # Sidebar navigation682 st.sidebar.title("Navigation")683 page = st.sidebar.selectbox(684 "Select Page",685 ["Dashboard", "Data Ingestion", "Alpaca Integration", "FinRL Training", "Trading Controls", "Portfolio Monitoring"]686 )687 688 # Display system status689 self.display_system_status()690 691 # Page routing692 if page == "Dashboard":693 st.header("๐ Dashboard")694 695 if self.config:696 st.subheader("System Configuration")697 config_col1, config_col2 = st.columns(2)698 699 with config_col1:700 st.write(f"**Symbol:** {self.config['trading']['symbol']}")701 st.write(f"**Capital:** ${self.config['trading']['capital']:,}")702 st.write(f"**Timeframe:** {self.config['trading']['timeframe']}")703 704 with config_col2:705 st.write(f"**Broker:** {self.config['execution']['broker_api']}")706 st.write(f"**FinRL Algorithm:** {self.config['finrl']['algorithm']}")707 st.write(f"**Risk Max Drawdown:** {self.config['risk']['max_drawdown']:.1%}")708 709 # Quick actions710 st.subheader("Quick Actions")711 col1, col2, col3 = st.columns(3)712 713 with col1:714 if st.button("Load Data", type="primary"):715 if self.config:716 with st.spinner("Loading data..."):717 self.data = load_data(self.config)718 if self.data is not None:719 st.success("โ
Data loaded successfully")720 721 with col2:722 if st.button("Connect Alpaca"):723 if self.config and self.config['execution']['broker_api'] in ['alpaca_paper', 'alpaca_live']:724 with st.spinner("Connecting..."):725 self.alpaca_broker = AlpacaBroker(self.config)726 st.success("โ
Connected to Alpaca")727 728 with col3:729 if st.button("Start Training"):730 if self.data is not None:731 st.info("Navigate to FinRL Training page to configure and start training")732 733 elif page == "Data Ingestion":734 self.data_ingestion_panel()735 736 elif page == "Alpaca Integration":737 self.alpaca_integration_panel()738 739 elif page == "FinRL Training":740 self.finrl_training_panel()741 742 elif page == "Trading Controls":743 self.trading_controls_panel()744 745 elif page == "Portfolio Monitoring":746 self.portfolio_monitoring_panel()747 748def main():749 """Main application entry point"""750 ui = TradingUI()751 ui.run()752 753def create_streamlit_app():754 """Create and return a Streamlit trading application"""755 return TradingUI()756 757if __name__ == "__main__":758 main() 