import streamlit as st import pandas as pd from database.database import get_session from database.models import User from services.compatibility_layer import ( get_env_var, set_env_var, get_all_env_vars, get_priority_stocks, get_all_stocks_with_prices, get_all_orders, get_all_trades, get_recent_priority_updates, get_user_by_username, verify_user_password, get_user_role ) # Page configuration st.set_page_config( page_title="Foundation", page_icon="âœī¸", layout="wide" ) # Custom CSS for Beautiful Light Jesus Theme st.markdown(""" """, unsafe_allow_html=True) def initialize_session_state(): """Initialize session state variables""" if 'authenticated' not in st.session_state: st.session_state.authenticated = False if 'user' not in st.session_state: st.session_state.user = None if 'user_role' not in st.session_state: st.session_state.user_role = None if 'engine_status' not in st.session_state: st.session_state.engine_status = 'Stopped' def authenticate_user(username, password): """Authenticate user against database""" session = get_session() try: user = get_user_by_username(session, username) if user and verify_user_password(user, password): # Get role name before session closes role_name = get_user_role(user) return user, role_name return None, None finally: session.close() def show_login_page(): """Simple login form""" st.title("âœī¸ Foundation - Login") with st.form("login_form"): username = st.text_input("Username") password = st.text_input("Password", type="password") submit = st.form_submit_button("Login") if submit: user, role_name = authenticate_user(username, password) if user: st.session_state.authenticated = True st.session_state.user = user st.session_state.user_role = role_name st.success("Login successful!") st.rerun() else: st.error("Invalid credentials") def show_trading_interface(): """Core trading interface per requirements""" st.title("💹 Algorithmic Trading System") # Trading engine controls col1, col2 = st.columns(2) with col1: engine_status = st.session_state.get('engine_status', 'Stopped') if engine_status == "Running": st.success("🤖 Trading Engine: RUNNING") # Live activity indicator import time current_time = time.time() if 'last_blink' not in st.session_state: st.session_state.last_blink = current_time # Blinking activity indicator if (current_time - st.session_state.last_blink) % 2 < 1: st.markdown("đŸŸĸ **LIVE** - Monitoring markets...") else: st.markdown("đŸ”ĩ **ACTIVE** - Analyzing stocks...") # Show last activity timestamp if 'last_activity' in st.session_state: st.caption(f"Last cycle: {st.session_state.last_activity}") if st.button("âšī¸ Stop Engine"): if st.session_state.get('trading_engine'): st.session_state.trading_engine.stop_trading() st.session_state.trading_engine = None st.session_state.engine_status = "Stopped" st.session_state.pop('last_activity', None) st.rerun() else: st.warning("🤖 Trading Engine: STOPPED") st.markdown("âšĢ **IDLE** - Ready to start...") if st.button("â–ļī¸ Start Engine"): from services.trading_engine import TradingEngine st.session_state.trading_engine = TradingEngine() st.session_state.trading_engine.start_trading() st.session_state.engine_status = "Running" st.session_state.last_activity = "Just started..." st.rerun() with col2: # Trading mode from environment variables session = get_session() try: mode_var = get_env_var(session, 'TRADING_MODE') current_mode = mode_var.value if mode_var else 'paper' st.info(f"Mode: {current_mode.upper()}") finally: session.close() # Real-time engine activity section if engine_status == "Running": st.subheader("đŸ”Ĩ Live Engine Activity") # Create placeholder for real-time updates activity_container = st.empty() # Show recent activity logs session = get_session() try: # Get most recent priority updates (last 5 minutes) recent_updates = get_recent_priority_updates(session, minutes=5, limit=10) if recent_updates: activity_data = [] for update in recent_updates: activity_data.append({ "Time": update["datetime"].strftime("%H:%M:%S"), "Symbol": update["symbol"], "Price": f"${update['current_price']:.2f}", "Change": f"{update['percent_change_from_previous']:+.2f}%", "Volume": f"{update['volume']:,}" if update['volume'] else "N/A" }) activity_df = pd.DataFrame(activity_data) with activity_container: st.success("📊 **Recent Price Updates** (Live monitoring in progress)") st.dataframe(activity_df, use_container_width=True) else: with activity_container: st.info("🔍 **Engine Starting...** Waiting for first price updates...") except Exception as e: with activity_container: st.warning(f"📡 **Connecting to market data...** ({str(e)[:50]})") finally: session.close() # Auto-refresh every 10 seconds when engine is running import time time.sleep(0.1) # Small delay to prevent too frequent updates if st.session_state.get('engine_status') == "Running": st.rerun() # Priority stocks (per requirements - stocks with priority > 0) st.subheader("đŸŽ¯ Priority Stocks (Algorithm Targets)") session = get_session() try: priority_stocks = get_priority_stocks(session) if priority_stocks: stock_data = [] for stock in priority_stocks: # Add visual indicator if recently updated last_update = "đŸŸĸ" if engine_status == "Running" else "âšĢ" stock_data.append({ "Status": last_update, "Symbol": stock.symbol, "Priority": stock.priority, "Last Price": f"${stock.last_price:.2f}" if stock.last_price else "N/A", "Change %": f"{stock.change_percent:.2f}%" if stock.change_percent else "N/A", "Has Options": "Yes" if stock.has_options else "No", "Sector": stock.sector }) df = pd.DataFrame(stock_data) st.dataframe(df, use_container_width=True) else: st.info("No priority stocks. Engine will identify trading opportunities automatically.") finally: session.close() def show_orders_page(): """Display orders table per requirements""" st.title("📋 Orders") session = get_session() try: orders = get_all_orders(session, limit=200) if orders: order_data = [] for order in orders: order_data.append({ "Symbol": order.symbol, "Action": order.side.title(), "Asset": order.asset_type.title(), "Quantity": order.quantity, "Order Type": order.order_type.title(), "Limit Price": f"${order.limit_price:.2f}" if order.limit_price else "N/A", "Status": order.status.title(), "Submitted": order.submitted_at.strftime("%Y-%m-%d %H:%M"), "Filled": order.filled_at.strftime("%Y-%m-%d %H:%M") if order.filled_at else "N/A" }) df = pd.DataFrame(order_data) st.dataframe(df, use_container_width=True) else: st.info("No orders found.") finally: session.close() def show_positions_page(): """Display positions and transaction log with LIFO gain/loss""" st.title("đŸ’ŧ Positions & Transaction Log") session = get_session() try: # Get live positions from active broker from services.broker_apis import BrokerManager # Use cached broker manager or create new one if 'broker_manager' not in st.session_state: st.session_state.broker_manager = BrokerManager() broker_manager = st.session_state.broker_manager broker_manager.authenticate_all() # Show live broker positions st.subheader("Live Broker Positions") try: live_positions = broker_manager.get_positions() account_info = broker_manager.get_account_info() # Display account summary if account_info and not account_info.get('error'): col1, col2, col3 = st.columns(3) with col1: st.metric("Cash Balance", f"${account_info.get('cash', 0):,.2f}") with col2: st.metric("Portfolio Value", f"${account_info.get('portfolio_value', 0):,.2f}") with col3: st.metric("Buying Power", f"${account_info.get('buying_power', 0):,.2f}") if live_positions: pos_data = [] for pos in live_positions: pos_data.append({ "Symbol": pos['symbol'], "Quantity": pos['quantity'], "Market Value": f"${pos.get('market_value', 0):,.2f}", "Cost Basis": f"${pos.get('cost_basis', 0):,.2f}", "Unrealized P&L": f"${pos.get('unrealized_pnl', 0):,.2f}", "Side": pos.get('side', 'long').title() }) df = pd.DataFrame(pos_data) st.dataframe(df, use_container_width=True) else: st.info("No live positions found in your broker account.") except Exception as e: st.error(f"Error fetching live positions: {e}") st.info("Showing positions from transaction log instead...") # Fallback: Calculate positions from trade history trades = get_all_trades(session, limit=500) positions = {} for trade in trades: key = f"{trade.symbol}" if key not in positions: positions[key] = { 'symbol': trade.symbol, 'quantity': 0, 'avg_price': 0, 'total_cost': 0 } pos = positions[key] if trade.side == 'BUY': pos['quantity'] += trade.quantity pos['total_cost'] += trade.executed_price * trade.quantity else: pos['quantity'] -= trade.quantity pos['total_cost'] -= trade.executed_price * trade.quantity if pos['quantity'] > 0: pos['avg_price'] = pos['total_cost'] / pos['quantity'] # Show database positions active_positions = [pos for pos in positions.values() if pos['quantity'] != 0] if active_positions: pos_data = [] for pos in active_positions: pos_data.append({ "Symbol": pos['symbol'], "Quantity": pos['quantity'], "Avg Price": f"${pos['avg_price']:.2f}", "Total Cost": f"${pos['total_cost']:.2f}" }) df = pd.DataFrame(pos_data) st.dataframe(df, use_container_width=True) else: st.info("No positions found.") # Transaction log with LIFO gain/loss calculations st.subheader("Transaction Log") recent_transactions = get_all_trades(session, limit=50) if recent_transactions: trans_data = [] for transaction in recent_transactions: trans_data.append({ "Date": transaction.transaction_date.strftime("%Y-%m-%d %H:%M"), "Symbol": transaction.symbol, "Side": transaction.side.title(), "Quantity": transaction.quantity, "Price": f"${transaction.executed_price:.2f}", "Total": f"${transaction.executed_price * transaction.quantity:.2f}" }) df = pd.DataFrame(trans_data) st.dataframe(df, use_container_width=True) else: st.info("No transactions found.") finally: session.close() def show_settings_page(): """Environment variables configuration per requirements""" user = st.session_state.user user_role = st.session_state.user_role if user_role == 'admin': st.title("âš™ī¸ Settings - Environment Variables") else: st.title("âš™ī¸ Trading Settings") session = get_session() try: # Get current environment variables env_vars = {var.key: var.value for var in get_all_env_vars(session)} st.subheader("Trading Configuration") # Show current broker status from services.broker_apis import BrokerManager if 'broker_manager' not in st.session_state: st.session_state.broker_manager = BrokerManager() current_broker = st.session_state.broker_manager.get_active_broker_name() st.info(f"🔄 **Current Active Broker**: {current_broker.replace('_', ' ').title()}") # Broker and trading mode - available to traders and admins col1, col2 = st.columns(2) with col1: # Broker selection active_broker = st.selectbox( "Active Broker", ["alpaca", "robinhood"], index=0 if env_vars.get('ACTIVE_BROKER', 'alpaca') == 'alpaca' else 1, help="Switch between different broker platforms for trading" ) trading_mode = st.selectbox( "Trading Mode (Paper/Live)", ["paper", "live"], index=0 if env_vars.get('TRADING_MODE', 'paper') == 'paper' else 1, help="Paper trading uses simulated money, Live trading uses real money" ) # Advanced settings - admin only if user_role == 'admin': with col2: price_interval = st.number_input( "Price Update Interval (seconds)", min_value=5, max_value=300, value=int(env_vars.get('PRICE_UPDATE_INTERVAL', '30')) ) col3, col4 = st.columns(2) with col3: max_position = st.number_input( "Max Position Size (%)", min_value=1.0, max_value=25.0, value=float(env_vars.get('MAX_POSITION_SIZE_PERCENT', '5.0')), step=0.5 ) with col4: archive_days = st.number_input( "Archive Retention (days)", min_value=7, max_value=365, value=int(env_vars.get('ARCHIVE_RETENTION_DAYS', '30')) ) if st.button("Save Settings"): # Update environment variables based on user role if user_role == 'admin': # Admin can change all settings updates = [ ('ACTIVE_BROKER', active_broker), ('TRADING_MODE', trading_mode), ('PRICE_UPDATE_INTERVAL', str(price_interval)), ('MAX_POSITION_SIZE_PERCENT', str(max_position)), ('ARCHIVE_RETENTION_DAYS', str(archive_days)) ] else: # Traders can only change broker and trading mode updates = [ ('ACTIVE_BROKER', active_broker), ('TRADING_MODE', trading_mode) ] for key, value in updates: set_env_var(session, key, value) # Immediately update the broker manager try: if 'broker_manager' in st.session_state: st.session_state.broker_manager.reload_configuration() else: st.session_state.broker_manager = BrokerManager() # Test the new broker connection new_broker = st.session_state.broker_manager.get_active_broker_name() st.success(f"✅ Settings updated! Switched to {new_broker.replace('_', ' ').title()}") # Show connection test with st.spinner("Testing new broker connection..."): try: account_info = st.session_state.broker_manager.get_account_info() if account_info and not account_info.get('error'): st.success(f"✅ Connection successful! Cash: ${account_info.get('cash', 0):,.2f}") else: st.warning("âš ī¸ Connection test failed - check broker credentials") except Exception as conn_error: st.warning(f"âš ī¸ Connection test error: {conn_error}") except Exception as e: st.error(f"❌ Error switching broker: {e}") st.rerun() finally: session.close() def sidebar_navigation(): """Streamlined navigation per requirements""" st.sidebar.title("âœī¸ Foundation") if st.session_state.authenticated: user = st.session_state.user user_role = st.session_state.user_role st.sidebar.write(f"👤 {user.username} ({user_role})") # Show engine status engine_status = st.session_state.get('engine_status', 'Stopped') if engine_status == "Running": st.sidebar.success("🤖 Engine: RUNNING") else: st.sidebar.warning("🤖 Engine: STOPPED") # Core navigation per original requirements pages = { "Trading": "💹", "Orders": "📋", "Positions": "đŸ’ŧ" } # Trader and Admin pages if user_role in ['admin', 'trader']: pages["Settings"] = "âš™ī¸" # Admin-only pages if user_role == 'admin': pages["Database"] = "đŸ—„ī¸" selected_page = st.sidebar.radio( "Navigation", list(pages.keys()), format_func=lambda x: f"{pages[x]} {x}" ) # Logout if st.sidebar.button("đŸšĒ Logout"): st.session_state.authenticated = False st.session_state.user = None st.session_state.user_role = None st.session_state.trading_engine = None st.rerun() return selected_page return None def load_page_content(page_name): """Load core trading functionality only""" if page_name == "Trading": show_trading_interface() elif page_name == "Orders": show_orders_page() elif page_name == "Positions": show_positions_page() elif page_name == "Settings": show_settings_page() elif page_name == "Database": from app_pages.database_admin import render_database_admin_page render_database_admin_page() def main(): """Main application entry point""" initialize_session_state() if not st.session_state.authenticated: show_login_page() return # Show sidebar and load selected page selected_page = sidebar_navigation() if selected_page: load_page_content(selected_page) if __name__ == "__main__": main()