新建会话
Ctrl
K
Kimi Code 限时活动
历史会话
查看手机应用

Stock Trading Page & Game

Create an HTML web page for a simulated stock trading platform. The theme can switch between dark and light. The top of the page shows the user's available funds, total assets, market value, and daily gains/losses. The user can view stock quotes in a table with stock names, latest prices, and changes. There is a trading panel for buying and selling, with options for market or limit orders, and inputs for stock codes and quantities. The bottom of the page has a settings button, including theme switching, market status toggling, and clearing records.
HTML
预览
复制
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>TradeSim Pro | Live Market Simulation</title>
   <script src="https://cdn.tailwindcss.com"></script>
   <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
   <script>
       tailwind.config = {
           darkMode: 'class',
           theme: {
               extend: {
                   colors: {
                       dark: {
                           bg: '#0f172a',
                           card: '#1e293b',
                           border: '#334155'
                       },
                       light: {
                           bg: '#f8fafc',
                           card: '#ffffff',
                           border: '#e2e8f0'
                       },
                       trade: {
                           up: '#10b981', // Emerald 500
                           down: '#ef4444', // Red 500
                           accent: '#3b82f6' // Blue 500
                       }
                   },
                   fontFamily: {
                       mono: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', "Liberation Mono", "Courier New", 'monospace'],
                   },
                   animation: {
                       'pulse-fast': 'pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite',
                       'flash-green': 'flashGreen 0.5s ease-out',
                       'flash-red': 'flashRed 0.5s ease-out',
                   },
                   keyframes: {
                       flashGreen: {
                           '0%': { backgroundColor: 'rgba(16, 185, 129, 0.2)' },
                           '100%': { backgroundColor: 'transparent' },
                       },
                       flashRed: {
                           '0%': { backgroundColor: 'rgba(239, 68, 68, 0.2)' },
                           '100%': { backgroundColor: 'transparent' },
                       }
                   }
               }
           }
       }
   </script>
   <style>
       /* Custom Scrollbar */
       ::-webkit-scrollbar {
           width: 8px;
           height: 8px;
       }
       ::-webkit-scrollbar-track {
           background: transparent;
       }
       ::-webkit-scrollbar-thumb {
           background: #475569;
           border-radius: 4px;
       }
       ::-webkit-scrollbar-thumb:hover {
           background: #64748b;
       }
       
       .glass-panel {
           backdrop-filter: blur(12px);
           -webkit-backdrop-filter: blur(12px);
       }

       /* Toggle Switch */
       .toggle-checkbox:checked {
           right: 0;
           border-color: #3b82f6;
       }
       .toggle-checkbox:checked + .toggle-label {
           background-color: #3b82f6;
       }
       
       /* CRT Effect for Dark Mode (Optional subtle scanline) */
       .dark .crt::before {
           content: " ";
           display: block;
           position: absolute;
           top: 0;
           left: 0;
           bottom: 0;
           right: 0;
           background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.1) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.03), rgba(0, 255, 0, 0.01), rgba(0, 0, 255, 0.03));
           z-index: 2;
           background-size: 100% 2px, 3px 100%;
           pointer-events: none;
       }
   </style>
</head>
<body class="bg-light-bg text-slate-800 dark:bg-dark-bg dark:text-slate-100 transition-colors duration-300 font-sans h-screen flex flex-col overflow-hidden selection:bg-blue-500 selection:text-white">

   <!-- Top Navigation / Header -->
   <header class="h-16 border-b border-light-border dark:border-dark-border bg-light-card dark:bg-dark-card flex items-center justify-between px-6 shadow-sm z-20 relative">
       <div class="flex items-center gap-3">
           <div class="w-8 h-8 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-lg flex items-center justify-center text-white font-bold shadow-lg shadow-blue-500/30">
               T
           </div>
           <h1 class="text-xl font-bold tracking-tight">Trade<span class="text-blue-500">Sim</span> Pro</h1>
           <span id="market-status-badge" class="ml-4 px-2 py-0.5 rounded text-xs font-medium bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800 flex items-center gap-1.5">
               <span class="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse"></span> MARKET OPEN
           </span>
       </div>

       <!-- Portfolio Summary (Top) -->
       <div class="hidden md:flex items-center gap-8 text-sm">
           <div class="flex flex-col items-end">
               <span class="text-slate-500 dark:text-slate-400 text-xs uppercase tracking-wider">Available Funds</span>
               <span id="header-funds" class="font-mono font-semibold text-lg">$0.00</span>
           </div>
           <div class="w-px h-8 bg-slate-200 dark:bg-slate-700"></div>
           <div class="flex flex-col items-end">
               <span class="text-slate-500 dark:text-slate-400 text-xs uppercase tracking-wider">Total Equity</span>
               <span id="header-equity" class="font-mono font-bold text-lg">$0.00</span>
           </div>
           <div class="flex flex-col items-end">
               <span class="text-slate-500 dark:text-slate-400 text-xs uppercase tracking-wider">Day P/L</span>
               <div class="flex items-center gap-1">
                   <span id="header-pl-icon"></span>
                   <span id="header-pl" class="font-mono font-bold text-lg">$0.00</span>
               </div>
           </div>
       </div>

       <button id="settings-btn" class="p-2 rounded-full hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors text-slate-500 dark:text-slate-400">
           <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"></path><circle cx="12" cy="12" r="3"></circle></svg>
       </button>
   </header>

   <!-- Main Layout -->
   <div class="flex flex-1 overflow-hidden relative">
       
       <!-- Left Column: Market Watch & Chart (70%) -->
       <main class="flex-1 flex flex-col border-r border-light-border dark:border-dark-border min-w-0 bg-light-bg dark:bg-dark-bg relative">
           
           <!-- Chart Area -->
           <div class="h-1/2 border-b border-light-border dark:border-dark-border p-4 flex flex-col bg-light-card dark:bg-dark-card m-4 rounded-xl shadow-sm border border-light-border dark:border-dark-border">
               <div class="flex justify-between items-center mb-2">
                   <div>
                       <h2 id="chart-title" class="text-lg font-bold flex items-center gap-2">
                           <span class="text-slate-400">Select a stock</span>
                       </h2>
                       <p id="chart-subtitle" class="text-xs text-slate-500">--</p>
                   </div>
                   <div class="flex gap-2">
                       <button class="px-3 py-1 text-xs font-medium rounded bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700">1H</button>
                       <button class="px-3 py-1 text-xs font-medium rounded bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700">1D</button>
                       <button class="px-3 py-1 text-xs font-medium rounded bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400">1W</button>
                   </div>
               </div>
               <div class="flex-1 relative w-full h-full min-h-0">
                   <canvas id="mainChart"></canvas>
               </div>
           </div>

           <!-- Market Watch Table -->
           <div class="flex-1 overflow-hidden flex flex-col bg-light-bg dark:bg-dark-bg">
               <div class="px-6 py-3 border-b border-light-border dark:border-dark-border flex justify-between items-center bg-light-card dark:bg-dark-card">
                   <h3 class="font-semibold text-sm uppercase tracking-wider text-slate-500 dark:text-slate-400">Market Watch</h3>
                   <div class="relative">
                       <input type="text" placeholder="Search symbol..." class="bg-slate-100 dark:bg-slate-800 border-none rounded-md text-xs px-3 py-1.5 focus:ring-1 focus:ring-blue-500 outline-none w-48 text-slate-700 dark:text-slate-200">
                       <svg class="absolute right-2 top-1.5 w-4 h-4 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
                   </div>
               </div>
               <div class="overflow-y-auto flex-1">
                   <table class="w-full text-left border-collapse">
                       <thead class="bg-slate-50 dark:bg-slate-800/50 sticky top-0 z-10 backdrop-blur-sm">
                           <tr>
                               <th class="px-6 py-3 text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Symbol</th>
                               <th class="px-6 py-3 text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider text-right">Price</th>
                               <th class="px-6 py-3 text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider text-right">Change</th>
                               <th class="px-6 py-3 text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider text-right">Vol</th>
                               <th class="px-6 py-3 text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider text-center">Action</th>
                           </tr>
                       </thead>
                       <tbody id="stock-table-body" class="divide-y divide-light-border dark:divide-dark-border text-sm">
                           <!-- Rows generated by JS -->
                       </tbody>
                   </table>
               </div>
           </div>
       </main>

       <!-- Right Column: Trading & Portfolio (30%) -->
       <aside class="w-96 bg-light-card dark:bg-dark-card flex flex-col shadow-xl z-10">
           
           <!-- Trading Panel -->
           <div class="p-6 border-b border-light-border dark:border-dark-border">
               <div class="flex items-center justify-between mb-4">
                   <h3 class="font-bold text-lg">Trade</h3>
                   <div class="flex bg-slate-100 dark:bg-slate-800 p-1 rounded-lg">
                       <button id="btn-buy-mode" class="px-4 py-1 rounded-md text-xs font-bold bg-white dark:bg-slate-700 shadow-sm text-emerald-600 dark:text-emerald-400 transition-all">BUY</button>
                       <button id="btn-sell-mode" class="px-4 py-1 rounded-md text-xs font-bold text-slate-500 hover:text-slate-700 dark:hover:text-slate-300 transition-all">SELL</button>
                   </div>
               </div>

               <form id="trade-form" onsubmit="return false;" class="space-y-4">
                   <div>
                       <label class="block text-xs font-medium text-slate-500 dark:text-slate-400 mb-1">Symbol</label>
                       <div class="relative">
                           <input type="text" id="input-symbol" class="w-full bg-slate-50 dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg px-3 py-2 font-mono font-bold text-slate-800 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none uppercase" placeholder="e.g. AAPL">
                           <div id="symbol-search-results" class="absolute top-full left-0 w-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg mt-1 shadow-xl hidden z-50 max-h-40 overflow-y-auto"></div>
                       </div>
                   </div>

                   <div class="grid grid-cols-2 gap-4">
                       <div>
                           <label class="block text-xs font-medium text-slate-500 dark:text-slate-400 mb-1">Quantity</label>
                           <input type="number" id="input-qty" min="1" value="10" class="w-full bg-slate-50 dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg px-3 py-2 font-mono text-slate-800 dark:text-white focus:ring-2 focus:ring-blue-500 outline-none">
                       </div>
                       <div>
                           <label class="block text-xs font-medium text-slate-500 dark:text-slate-400 mb-1">Order Type</label>
                           <select id="input-type" class="w-full bg-slate-50 dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg px-3 py-2 text-sm text-slate-800 dark:text-white focus:ring-2 focus:ring-blue-500 outline-none appearance-none">
                               <option value="market">Market</option>
                               <option value="limit">Limit</option>
                           </select>
                       </div>
                   </div>

                   <div id="limit-price-container" class="hidden">
                       <label class="block text-xs font-medium text-slate-500 dark:text-slate-400 mb-1">Limit Price</label>
                       <input type="number" id="input-price" step="0.01" class="w-full bg-slate-50 dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg px-3 py-2 font-mono text-slate-800 dark:text-white focus:ring-2 focus:ring-blue-500 outline-none">
                   </div>

                   <div class="pt-2">
                       <div class="flex justify-between text-xs mb-1 text-slate-500 dark:text-slate-400">
                           <span>Est. Total</span>
                           <span id="est-total" class="font-mono font-bold text-slate-700 dark:text-slate-200">$0.00</span>
                       </div>
                       <button id="btn-execute" type="button" class="w-full py-3 rounded-lg font-bold text-white shadow-lg transform active:scale-95 transition-all bg-emerald-500 hover:bg-emerald-600 shadow-emerald-500/30">
                           BUY SHARES
                       </button>
                   </div>
               </form>
           </div>

           <!-- Portfolio Holdings -->
           <div class="flex-1 flex flex-col overflow-hidden">
               <div class="px-6 py-3 border-b border-light-border dark:border-dark-border bg-slate-50 dark:bg-slate-800/30">
                   <h3 class="font-semibold text-sm uppercase tracking-wider text-slate-500 dark:text-slate-400">Your Positions</h3>
               </div>
               <div class="overflow-y-auto flex-1 p-4 space-y-3" id="portfolio-list">
                   <!-- Portfolio items injected here -->
                   <div class="text-center py-10 text-slate-400 text-sm">
                       No open positions yet.
                   </div>
               </div>
           </div>
       </aside>
   </div>

   <!-- Settings Modal -->
   <div id="settings-modal" class="fixed inset-0 z-50 hidden">
       <div class="absolute inset-0 bg-black/50 backdrop-blur-sm transition-opacity" id="settings-backdrop"></div>
       <div class="absolute right-0 top-0 h-full w-80 bg-light-card dark:bg-dark-card shadow-2xl transform transition-transform translate-x-full duration-300 flex flex-col" id="settings-panel">
           <div class="p-6 border-b border-light-border dark:border-dark-border flex justify-between items-center">
               <h2 class="text-xl font-bold">Settings</h2>
               <button id="close-settings" class="text-slate-400 hover:text-slate-600 dark:hover:text-slate-200">
                   <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
               </button>
           </div>
           
           <div class="p-6 space-y-8 flex-1 overflow-y-auto">
               <!-- Theme Toggle -->
               <div class="flex items-center justify-between">
                   <div>
                       <h4 class="font-medium text-slate-900 dark:text-white">Dark Mode</h4>
                       <p class="text-xs text-slate-500">Toggle between light and dark themes</p>
                   </div>
                   <div class="relative inline-block w-12 mr-2 align-middle select-none transition duration-200 ease-in">
                       <input type="checkbox" name="toggle" id="theme-toggle" class="toggle-checkbox absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer transition-all duration-300 left-0 border-slate-300"/>
                       <label for="theme-toggle" class="toggle-label block overflow-hidden h-6 rounded-full bg-slate-300 cursor-pointer transition-colors duration-300"></label>
                   </div>
               </div>

               <!-- Market Status -->
               <div class="flex items-center justify-between">
                   <div>
                       <h4 class="font-medium text-slate-900 dark:text-white">Market Status</h4>
                       <p class="text-xs text-slate-500">Pause/Resume price updates</p>
                   </div>
                   <button id="toggle-market-btn" class="px-4 py-2 rounded-lg text-sm font-bold bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800">
                       PAUSE MARKET
                   </button>
               </div>

               <!-- Reset -->
               <div class="pt-6 border-t border-light-border dark:border-dark-border">
                   <h4 class="font-medium text-red-500 mb-2">Danger Zone</h4>
                   <button id="reset-btn" class="w-full py-2 border border-red-200 dark:border-red-900/50 text-red-500 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors text-sm font-medium">
                       Reset Portfolio & History
                   </button>
               </div>
           </div>
           
           <div class="p-6 border-t border-light-border dark:border-dark-border text-center text-xs text-slate-400">
               TradeSim Pro v1.0.2
           </div>
       </div>
   </div>

   <!-- Toast Notification Container -->
   <div id="toast-container" class="fixed bottom-6 right-6 z-50 flex flex-col gap-2 pointer-events-none"></div>

   <script>
       // --- Application State ---
       const state = {
           cash: 10000.00,
           holdings: {}, // { 'AAPL': { qty: 10, avgPrice: 150.00 } }
           marketOpen: true,
           theme: 'dark',
           selectedStock: null,
           tradeMode: 'buy', // 'buy' or 'sell'
           stocks: [
               { symbol: 'AAPL', name: 'Apple Inc.', price: 175.50, open: 174.00, vol: 54000000 },
               { symbol: 'MSFT', name: 'Microsoft Corp.', price: 330.20, open: 328.00, vol: 22000000 },
               { symbol: 'GOOGL', name: 'Alphabet Inc.', price: 135.80, open: 136.50, vol: 18000000 },
               { symbol: 'AMZN', name: 'Amazon.com', price: 128.90, open: 127.00, vol: 35000000 },
               { symbol: 'TSLA', name: 'Tesla Inc.', price: 245.60, open: 250.00, vol: 98000000 },
               { symbol: 'NVDA', name: 'NVIDIA Corp.', price: 460.10, open: 455.00, vol: 45000000 },
               { symbol: 'META', name: 'Meta Platforms', price: 305.40, open: 300.00, vol: 15000000 },
               { symbol: 'NFLX', name: 'Netflix Inc.', price: 440.20, open: 435.00, vol: 5000000 },
               { symbol: 'AMD', name: 'Adv. Micro Dev.', price: 105.30, open: 104.00, vol: 60000000 },
               { symbol: 'INTC', name: 'Intel Corp.', price: 35.40, open: 36.00, vol: 30000000 },
           ],
           history: [] // For chart
       };

       // --- DOM Elements ---
       const els = {
           headerFunds: document.getElementById('header-funds'),
           headerEquity: document.getElementById('header-equity'),
           headerPL: document.getElementById('header-pl'),
           headerPLIcon: document.getElementById('header-pl-icon'),
           tableBody: document.getElementById('stock-table-body'),
           chartCanvas: document.getElementById('mainChart'),
           chartTitle: document.getElementById('chart-title'),
           chartSubtitle: document.getElementById('chart-subtitle'),
           inputSymbol: document.getElementById('input-symbol'),
           inputQty: document.getElementById('input-qty'),
           inputType: document.getElementById('input-type'),
           inputPrice: document.getElementById('input-price'),
           limitContainer: document.getElementById('limit-price-container'),
           estTotal: document.getElementById('est-total'),
           btnExecute: document.getElementById('btn-execute'),
           btnBuyMode: document.getElementById('btn-buy-mode'),
           btnSellMode: document.getElementById('btn-sell-mode'),
           portfolioList: document.getElementById('portfolio-list'),
           settingsModal: document.getElementById('settings-modal'),
           settingsPanel: document.getElementById('settings-panel'),
           settingsBackdrop: document.getElementById('settings-backdrop'),
           settingsBtn: document.getElementById('settings-btn'),
           closeSettingsBtn: document.getElementById('close-settings'),
           themeToggle: document.getElementById('theme-toggle'),
           toggleMarketBtn: document.getElementById('toggle-market-btn'),
           marketStatusBadge: document.getElementById('market-status-badge'),
           resetBtn: document.getElementById('reset-btn'),
           symbolSearchResults: document.getElementById('symbol-search-results')
       };

       let chartInstance = null;

       // --- Initialization ---
       function init() {
           // Generate initial history for charts
           state.stocks.forEach(s => {
               s.history = generateRandomHistory(s.price, 50);
               s.prevPrice = s.price;
           });

           // Set initial selected stock
           selectStock(state.stocks[0]);

           // Render
           updatePortfolioUI();
           renderMarketTable();
           initChart();
           
           // Start Simulation
           setInterval(simulateMarket, 2000);

           // Event Listeners
           setupEventListeners();
           
           // Initial UI Update
           updateTradeUI();
       }

       // --- Logic: Market Simulation ---
       function generateRandomHistory(basePrice, count) {
           let data = [];
           let price = basePrice * 0.9;
           for(let i=0; i<count; i++) {
               price = price * (1 + (Math.random() * 0.04 - 0.02));
               data.push(price);
           }
           // Ensure last point connects to current
           data[data.length-1] = basePrice;
           return data;
       }

       function simulateMarket() {
           if (!state.marketOpen) return;

           state.stocks.forEach(stock => {
               stock.prevPrice = stock.price;
               // Random walk -2% to +2%
               const change = (Math.random() * 0.04) - 0.02;
               stock.price = stock.price * (1 + change);
               
               // Update history
               stock.history.shift();
               stock.history.push(stock.price);
               
               // Update Volume slightly
               stock.vol += Math.floor(Math.random() * 1000);
           });

           renderMarketTable();
           updateChart();
           updatePortfolioUI(); // To update live P/L on holdings
       }

       // --- Logic: Trading ---
       function executeTrade() {
           const symbol = els.inputSymbol.value.toUpperCase();
           const qty = parseInt(els.inputQty.value);
           const type = els.inputType.value;
           const mode = state.tradeMode;

           if (!symbol || !qty || qty <= 0) {
               showToast('Invalid input', 'error');
               return;
           }

           const stock = state.stocks.find(s => s.symbol === symbol);
           if (!stock) {
               showToast('Symbol not found', 'error');
               return;
           }

           let price = stock.price;
           if (type === 'limit') {
               const limit = parseFloat(els.inputPrice.value);
               if (!limit) {
                   showToast('Enter a limit price', 'error');
                   return;
               }
               // Simulation: Limit orders fill immediately if price is "reasonable" (within 1%)
               if (Math.abs(limit - price) / price > 0.01) {
                   showToast(`Limit order for ${symbol} placed (Simulated)`, 'info');
                   return; 
               }
               price = limit;
           }

           const total = price * qty;

           if (mode === 'buy') {
               if (total > state.cash) {
                   showToast('Insufficient funds', 'error');
                   return;
               }
               state.cash -= total;
               
               if (!state.holdings[symbol]) {
                   state.holdings[symbol] = { qty: 0, avgPrice: 0 };
               }
               // Weighted average cost basis
               const oldVal = state.holdings[symbol].qty * state.holdings[symbol].avgPrice;
               const newVal = qty * price;
               state.holdings[symbol].qty += qty;
               state.holdings[symbol].avgPrice = (oldVal + newVal) / state.holdings[symbol].qty;
               
               showToast(`Bought ${qty} shares of ${symbol} @ $${price.toFixed(2)}`, 'success');
           } else {
               // Sell
               if (!state.holdings[symbol] || state.holdings[symbol].qty < qty) {
                   showToast('Insufficient shares', 'error');
                   return;
               }
               state.cash += total;
               state.holdings[symbol].qty -= qty;
               if (state.holdings[symbol].qty === 0) {
                   delete state.holdings[symbol];
               }
               showToast(`Sold ${qty} shares of ${symbol} @ $${price.toFixed(2)}`, 'success');
           }

           updatePortfolioUI();
           renderPortfolioList();
       }

       // --- UI: Rendering ---
       function renderMarketTable() {
           els.tableBody.innerHTML = '';
           state.stocks.forEach(stock => {
               const change = stock.price - stock.open;
               const changePct = (change / stock.open) * 100;
               const isUp = change >= 0;
               const flashClass = stock.price > stock.prevPrice ? 'animate-flash-green' : (stock.price < stock.prevPrice ? 'animate-flash-red' : '');

               const tr = document.createElement('tr');
               tr.className = `hover:bg-slate-50 dark:hover:bg-slate-800/50 cursor-pointer transition-colors border-b border-light-border dark:border-dark-border ${flashClass}`;
               tr.onclick = () => selectStock(stock);
               if (state.selectedStock?.symbol === stock.symbol) {
                   tr.classList.add('bg-blue-50', 'dark:bg-blue-900/20');
               }

               tr.innerHTML = `
                   <td class="px-6 py-3">
                       <div class="flex items-center">
                           <div class="h-8 w-8 rounded-full bg-slate-200 dark:bg-slate-700 flex items-center justify-center text-xs font-bold mr-3 text-slate-600 dark:text-slate-300">
                               ${stock.symbol[0]}
                           </div>
                           <div>
                               <div class="font-bold text-slate-900 dark:text-white">${stock.symbol}</div>
                               <div class="text-xs text-slate-500">${stock.name}</div>
                           </div>
                       </div>
                   </td>
                   <td class="px-6 py-3 text-right font-mono font-medium text-slate-700 dark:text-slate-200">
                       $${stock.price.toFixed(2)}
                   </td>
                   <td class="px-6 py-3 text-right font-mono font-medium ${isUp ? 'text-emerald-500' : 'text-red-500'}">
                       ${isUp ? '+' : ''}${change.toFixed(2)} (${changePct.toFixed(2)}%)
                   </td>
                   <td class="px-6 py-3 text-right text-xs text-slate-500 font-mono">
                       ${(stock.vol / 1000000).toFixed(1)}M
                   </td>
                   <td class="px-6 py-3 text-center">
                       <button onclick="event.stopPropagation(); quickTrade('${stock.symbol}')" class="text-xs bg-slate-100 dark:bg-slate-800 hover:bg-blue-100 dark:hover:bg-blue-900/30 text-blue-600 dark:text-blue-400 px-2 py-1 rounded transition-colors">
                           Trade
                       </button>
                   </td>
               `;
               els.tableBody.appendChild(tr);
           });
       }

       function renderPortfolioList() {
           els.portfolioList.innerHTML = '';
           const symbols = Object.keys(state.holdings);
           
           if (symbols.length === 0) {
               els.portfolioList.innerHTML = `<div class="flex flex-col items-center justify-center h-full text-slate-400"><svg class="w-12 h-12 mb-2 opacity-20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg><p class="text-sm">No positions</p></div>`;
               return;
           }

           symbols.forEach(sym => {
               const pos = state.holdings[sym];
               const stock = state.stocks.find(s => s.symbol === sym);
               const currentPrice = stock ? stock.price : pos.avgPrice;
               const marketVal = pos.qty * currentPrice;
               const costBasis = pos.qty * pos.avgPrice;
               const pl = marketVal - costBasis;
               const plPct = (pl / costBasis) * 100;
               const isProfit = pl >= 0;

               const div = document.createElement('div');
               div.className = 'bg-slate-50 dark:bg-slate-800/50 rounded-lg p-3 border border-slate-100 dark:border-slate-700';
               div.innerHTML = `
                   <div class="flex justify-between items-start mb-1">
                       <div>
                           <span class="font-bold text-slate-900 dark:text-white">${sym}</span>
                           <span class="text-xs text-slate-500 ml-1">${pos.qty} shares</span>
                       </div>
                       <div class="text-right">
                           <div class="font-mono font-medium text-slate-800 dark:text-slate-200">$${marketVal.toFixed(2)}</div>
                           <div class="text-xs font-mono ${isProfit ? 'text-emerald-500' : 'text-red-500'}">
                               ${isProfit ? '+' : ''}$${pl.toFixed(2)} (${plPct.toFixed(1)}%)
                           </div>
                       </div>
                   </div>
                   <div class="w-full bg-slate-200 dark:bg-slate-700 h-1 rounded-full mt-2 overflow-hidden">
                       <div class="bg-blue-500 h-1 rounded-full" style="width: ${Math.min(Math.abs(plPct), 100)}%"></div>
                   </div>
               `;
               els.portfolioList.appendChild(div);
           });
       }

       function updatePortfolioUI() {
           // Calculate Totals
           let marketValue = 0;
           let dayPL = 0;

           Object.keys(state.holdings).forEach(sym => {
               const pos = state.holdings[sym];
               const stock = state.stocks.find(s => s.symbol === sym);
               if (stock) {
                   marketValue += pos.qty * stock.price;
                   dayPL += pos.qty * (stock.price - stock.open);
               }
           });

           const totalEquity = state.cash + marketValue;

           // Update Header
           els.headerFunds.innerText = formatCurrency(state.cash);
           els.headerEquity.innerText = formatCurrency(totalEquity);
           
           const plElem = els.headerPL;
           const plIcon = els.headerPLIcon;
           
           plElem.innerText = (dayPL >= 0 ? '+' : '') + formatCurrency(dayPL);
           plElem.className = `font-mono font-bold text-lg ${dayPL >= 0 ? 'text-emerald-500' : 'text-red-500'}`;
           
           plIcon.innerHTML = dayPL >= 0 
               ? '<svg class="w-4 h-4 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path></svg>'
               : '<svg class="w-4 h-4 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 17h8m0 0V9m0 8l-8-8-4 4-6-6"></path></svg>';

           renderPortfolioList();
       }

       function updateTradeUI() {
           const isBuy = state.tradeMode === 'buy';
           
           // Toggle Buttons
           if (isBuy) {
               els.btnBuyMode.className = "px-4 py-1 rounded-md text-xs font-bold bg-white dark:bg-slate-700 shadow-sm text-emerald-600 dark:text-emerald-400 transition-all";
               els.btnSellMode.className = "px-4 py-1 rounded-md text-xs font-bold text-slate-500 hover:text-slate-700 dark:hover:text-slate-300 transition-all";
               els.btnExecute.className = "w-full py-3 rounded-lg font-bold text-white shadow-lg transform active:scale-95 transition-all bg-emerald-500 hover:bg-emerald-600 shadow-emerald-500/30";
               els.btnExecute.innerText = "BUY SHARES";
           } else {
               els.btnSellMode.className = "px-4 py-1 rounded-md text-xs font-bold bg-white dark:bg-slate-700 shadow-sm text-red-600 dark:text-red-400 transition-all";
               els.btnBuyMode.className = "px-4 py-1 rounded-md text-xs font-bold text-slate-500 hover:text-slate-700 dark:hover:text-slate-300 transition-all";
               els.btnExecute.className = "w-full py-3 rounded-lg font-bold text-white shadow-lg transform active:scale-95 transition-all bg-red-500 hover:bg-red-600 shadow-red-500/30";
               els.btnExecute.innerText = "SELL SHARES";
           }

           // Calculation
           const qty = parseInt(els.inputQty.value) || 0;
           const symbol = els.inputSymbol.value.toUpperCase();
           const stock = state.stocks.find(s => s.symbol === symbol);
           
           let price = 0;
           if (els.inputType.value === 'market' && stock) {
               price = stock.price;
           } else {
               price = parseFloat(els.inputPrice.value) || 0;
           }

           const total = qty * price;
           els.estTotal.innerText = formatCurrency(total);
       }

       // --- Chart.js Integration ---
       function initChart() {
           const ctx = els.chartCanvas.getContext('2d');
           
           // Gradient
           const gradient = ctx.createLinearGradient(0, 0, 0, 400);
           gradient.addColorStop(0, 'rgba(59, 130, 246, 0.5)'); // Blue top
           gradient.addColorStop(1, 'rgba(59, 130, 246, 0)');   // Transparent bottom

           chartInstance = new Chart(ctx, {
               type: 'line',
               data: {
                   labels: Array(50).fill(''), // Dummy labels
                   datasets: [{
                       label: 'Price',
                       data: [],
                       borderColor: '#3b82f6',
                       backgroundColor: gradient,
                       borderWidth: 2,
                       pointRadius: 0,
                       pointHoverRadius: 4,
                       fill: true,
                       tension: 0.4
                   }]
               },
               options: {
                   responsive: true,
                   maintainAspectRatio: false,
                   plugins: {
                       legend: { display: false },
                       tooltip: {
                           mode: 'index',
                           intersect: false,
                           callbacks: {
                               label: function(context) {
                                   return '$' + context.parsed.y.toFixed(2);
                               }
                           }
                       }
                   },
                   scales: {
                       x: { display: false },
                       y: {
                           display: true,
                           position: 'right',
                           grid: {
                               color: 'rgba(148, 163, 184, 0.1)'
                           },
                           ticks: {
                               color: '#64748b',
                               callback: function(value) { return '$' + value; }
                           }
                       }
                   },
                   interaction: {
                       mode: 'nearest',
                       axis: 'x',
                       intersect: false
                   }
               }
           });
       }

       function updateChart() {
           if (!state.selectedStock || !chartInstance) return;
           
           const stock = state.selectedStock;
           chartInstance.data.datasets[0].data = stock.history;
           
           // Dynamic color based on trend
           const isUp = stock.price >= stock.open;
           const color = isUp ? '#10b981' : '#ef4444';
           
           chartInstance.data.datasets[0].borderColor = color;
           
           // Update gradient
           const ctx = els.chartCanvas.getContext('2d');
           const gradient = ctx.createLinearGradient(0, 0, 0, 400);
           gradient.addColorStop(0, isUp ? 'rgba(16, 185, 129, 0.5)' : 'rgba(239, 68, 68, 0.5)');
           gradient.addColorStop(1, 'rgba(0,0,0,0)');
           chartInstance.data.datasets[0].backgroundColor = gradient;

           chartInstance.update('none'); // 'none' for performance
       }

       function selectStock(stock) {
           state.selectedStock = stock;
           els.inputSymbol.value = stock.symbol;
           
           // Update Chart Header
           const change = stock.price - stock.open;
           const changePct = (change / stock.open) * 100;
           const isUp = change >= 0;
           
           els.chartTitle.innerHTML = `
               <span class="text-slate-900 dark:text-white">${stock.symbol}</span>
               <span class="text-sm font-normal text-slate-500 ml-2">${stock.name}</span>
               <span class="ml-3 text-sm font-mono ${isUp ? 'text-emerald-500' : 'text-red-500'}">
                   ${isUp ? '▲' : '▼'} ${Math.abs(change).toFixed(2)} (${Math.abs(changePct).toFixed(2)}%)
               </span>
           `;
           els.chartSubtitle.innerText = `Last updated: ${new Date().toLocaleTimeString()}`;

           updateChart();
           renderMarketTable(); // To update highlighting
       }

       // --- Utilities ---
       function formatCurrency(num) {
           return '$' + num.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');
       }

       function showToast(message, type = 'info') {
           const toast = document.createElement('div');
           const colors = {
               success: 'bg-emerald-500',
               error: 'bg-red-500',
               info: 'bg-blue-500'
           };
           
           toast.className = `${colors[type]} text-white px-4 py-3 rounded-lg shadow-lg transform transition-all duration-300 translate-y-10 opacity-0 flex items-center gap-2 pointer-events-auto min-w-[200px]`;
           toast.innerHTML = `
               <span class="font-medium text-sm">${message}</span>
           `;
           
           document.getElementById('toast-container').appendChild(toast);
           
           // Animate in
           requestAnimationFrame(() => {
               toast.classList.remove('translate-y-10', 'opacity-0');
           });

           // Remove after 3s
           setTimeout(() => {
               toast.classList.add('translate-y-10', 'opacity-0');
               setTimeout(() => toast.remove(), 300);
           }, 3000);
       }

       function quickTrade(symbol) {
           els.inputSymbol.value = symbol;
           // Scroll to trade panel on mobile if needed, or just focus
           els.inputQty.focus();
           showToast(`Selected ${symbol}. Review quantity and click Buy/Sell.`, 'info');
       }

       // --- Event Listeners ---
       function setupEventListeners() {
           // Trade Form Inputs
           [els.inputQty, els.inputSymbol, els.inputPrice, els.inputType].forEach(el => {
               el.addEventListener('input', updateTradeUI);
           });

           // Buy/Sell Mode Toggle
           els.btnBuyMode.addEventListener('click', () => {
               state.tradeMode = 'buy';
               updateTradeUI();
           });
           els.btnSellMode.addEventListener('click', () => {
               state.tradeMode = 'sell';
               updateTradeUI();
           });

           // Execute Trade
           els.btnExecute.addEventListener('click', executeTrade);

           // Limit Order Toggle
           els.inputType.addEventListener('change', (e) => {
               if (e.target.value === 'limit') {
                   els.limitContainer.classList.remove('hidden');
                   if(state.selectedStock) els.inputPrice.value = state.selectedStock.price.toFixed(2);
               } else {
                   els.limitContainer.classList.add('hidden');
               }
               updateTradeUI();
           });

           // Settings Modal
           els.settingsBtn.addEventListener('click', () => {
               els.settingsModal.classList.remove('hidden');
               setTimeout(() => {
                   els.settingsPanel.classList.remove('translate-x-full');
               }, 10);
           });

           const closeSettings = () => {
               els.settingsPanel.classList.add('translate-x-full');
               setTimeout(() => {
                   els.settingsModal.classList.add('hidden');
               }, 300);
           };

           els.closeSettingsBtn.addEventListener('click', closeSettings);
           els.settingsBackdrop.addEventListener('click', closeSettings);

           // Theme Toggle
           els.themeToggle.checked = document.documentElement.classList.contains('dark');
           els.themeToggle.addEventListener('change', (e) => {
               if (e.target.checked) {
                   document.documentElement.classList.add('dark');
               } else {
                   document.documentElement.classList.remove('dark');
               }
           });

           // Market Toggle
           els.toggleMarketBtn.addEventListener('click', () => {
               state.marketOpen = !state.marketOpen;
               if (state.marketOpen) {
                   els.toggleMarketBtn.innerText = "PAUSE MARKET";
                   els.toggleMarketBtn.className = "px-4 py-2 rounded-lg text-sm font-bold bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800";
                   els.marketStatusBadge.innerHTML = `<span class="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse"></span> MARKET OPEN`;
                   showToast('Market Resumed', 'success');
               } else {
                   els.toggleMarketBtn.innerText = "RESUME MARKET";
                   els.toggleMarketBtn.className = "px-4 py-2 rounded-lg text-sm font-bold bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400 border border-amber-200 dark:border-amber-800";
                   els.marketStatusBadge.innerHTML = `<span class="w-1.5 h-1.5 rounded-full bg-amber-500"></span> MARKET PAUSED`;
                   showToast('Market Paused', 'info');
               }
           });

           // Reset
           els.resetBtn.addEventListener('click', () => {
               if(confirm('Reset all data? This cannot be undone.')) {
                   state.cash = 10000;
                   state.holdings = {};
                   updatePortfolioUI();
                   showToast('Portfolio Reset', 'success');
               }
           });

           // Symbol Search (Simple Filter)
           els.inputSymbol.addEventListener('input', (e) => {
               const val = e.target.value.toUpperCase();
               if (val.length < 1) {
                   els.symbolSearchResults.classList.add('hidden');
                   return;
               }
               
               const matches = state.stocks.filter(s => s.symbol.includes(val) || s.name.toUpperCase().includes(val));
               els.symbolSearchResults.innerHTML = '';
               
               if (matches.length > 0) {
                   els.symbolSearchResults.classList.remove('hidden');
                   matches.forEach(s => {
                       const div = document.createElement('div');
                       div.className = "px-4 py-2 hover:bg-slate-100 dark:hover:bg-slate-700 cursor-pointer text-sm text-slate-700 dark:text-slate-200";
                       div.innerText = `${s.symbol} - ${s.name}`;
                       div.onclick = () => {
                           els.inputSymbol.value = s.symbol;
                           els.symbolSearchResults.classList.add('hidden');
                           selectStock(s);
                           updateTradeUI();
                       };
                       els.symbolSearchResults.appendChild(div);
                   });
               } else {
                   els.symbolSearchResults.classList.add('hidden');
               }
           });
           
           // Hide search results on click outside
           document.addEventListener('click', (e) => {
               if (!els.inputSymbol.contains(e.target) && !els.symbolSearchResults.contains(e.target)) {
                   els.symbolSearchResults.classList.add('hidden');
               }
           });
       }

       // Run
       document.addEventListener('DOMContentLoaded', init);

   </script>
</body>
</html>