/* * Quantitative Trading Platform - Interactive Mockup * Main JavaScript for navigation and interactivity */ // Global state const AppState = { currentSection: 'dashboard', charts: {}, mockData: {} }; // Initialize the application function initApp() { console.log('Quantitative Trading Platform Mockup Initialized'); // Set up navigation setupNavigation(); // Load mock data loadMockData(); // Initialize charts initCharts(); // Show default section showSection('dashboard'); // Set up event listeners for interactive elements setupEventListeners(); } // Set up sidebar navigation function setupNavigation() { const navLinks = document.querySelectorAll('.nav-link[data-section]'); navLinks.forEach(link => { link.addEventListener('click', function(e) { e.preventDefault(); const section = this.getAttribute('data-section'); showSection(section); // Update active state navLinks.forEach(l => l.classList.remove('active')); this.classList.add('active'); }); }); } // Show a specific section function showSection(sectionId) { // Hide all sections const sections = document.querySelectorAll('.section-content'); sections.forEach(section => { section.classList.add('d-none'); }); // Show the selected section const targetSection = document.getElementById(`${sectionId}-section`); if (targetSection) { targetSection.classList.remove('d-none'); AppState.currentSection = sectionId; // Update page title const sectionTitle = targetSection.querySelector('h1, h2')?.textContent || 'Quantitative Trading Platform'; document.title = `${sectionTitle} - Mockup`; // Refresh charts for the section if needed refreshSectionCharts(sectionId); } } // Refresh charts for a specific section function refreshSectionCharts(sectionId) { switch(sectionId) { case 'dashboard': if (AppState.charts.portfolioChart) { updatePortfolioChart(); } break; case 'data-management': if (AppState.charts.dataQualityChart) { updateDataQualityChart(); } break; case 'strategy-engine': if (AppState.charts.backtestChart) { updateBacktestChart(); } break; case 'risk-management': if (AppState.charts.riskChart) { updateRiskChart(); } break; } } // Set up event listeners for buttons and interactive elements function setupEventListeners() { // Refresh buttons const refreshButtons = document.querySelectorAll('button:contains("Refresh"), .btn-refresh, .btn-outline-light'); refreshButtons.forEach(btn => { // Look for the refresh button specifically if (btn.querySelector('.bi-arrow-clockwise') || btn.textContent.includes('Refresh')) { btn.addEventListener('click', function() { // Fetch real market data from backend const symbol = 'AAPL'; // Default for now fetch(`/api/market-data/${symbol}`) .then(response => response.json()) .then(data => { // Update UI with real data updateDashboardWithRealData(data); showToast(`Data refreshed for ${symbol}`, 'success'); }) .catch(error => { console.error('Error fetching data:', error); showToast('Failed to refresh data', 'danger'); }); }); } }); // Strategy config form submission (real backend call) const strategyForm = document.getElementById('strategy-config-form'); if (strategyForm) { strategyForm.addEventListener('submit', function(e) { e.preventDefault(); const payload = { name: document.getElementById('strategy-name').value, type: document.getElementById('strategy-type').value, initialCapital: Number(document.getElementById('initial-capital').value), entryLookback: Number(document.getElementById('entry-lookback').value), exitLookback: Number(document.getElementById('exit-lookback').value), riskPerTrade: Number(document.getElementById('risk-per-trade').value), maxPositions: Number(document.getElementById('max-positions').value), notes: document.getElementById('strategy-notes').value, }; fetch('/api/strategy-config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }) .then(res => res.json()) .then(data => { if (data.status === 'ok') { showToast('Strategy configuration saved', 'success'); } else { showToast('Failed to save configuration', 'danger'); } }) .catch(err => { console.error('Error saving strategy config', err); showToast('Error saving configuration', 'danger'); }); }); } // Generic form submissions (fallback) const forms = document.querySelectorAll('form'); forms.forEach(form => { if (form.id !== 'strategy-config-form') { form.addEventListener('submit', function(e) { e.preventDefault(); showToast('Settings saved successfully', 'success'); }); } }); // Toggle buttons const toggleButtons = document.querySelectorAll('.btn[data-toggle]'); toggleButtons.forEach(btn => { btn.addEventListener('click', function() { const target = this.getAttribute('data-toggle'); const state = this.getAttribute('data-state'); if (target && state) { // Toggle state const newState = state === 'on' ? 'off' : 'on'; this.setAttribute('data-state', newState); // Update button appearance if (newState === 'on') { this.classList.remove('btn-outline-secondary'); this.classList.add('btn-success'); this.innerHTML = ' ON'; } else { this.classList.remove('btn-success'); this.classList.add('btn-outline-secondary'); this.innerHTML = ' OFF'; } showToast(`Feature ${newState === 'on' ? 'enabled' : 'disabled'}`, 'info'); } }); }); // Tab clicks const tabs = document.querySelectorAll('[data-bs-toggle="tab"]'); tabs.forEach(tab => { tab.addEventListener('shown.bs.tab', function() { const tabId = this.getAttribute('data-bs-target'); console.log(`Tab activated: ${tabId}`); }); }); // Simulate live data updates setInterval(updateLiveData, 5000); } // Update live data (simulated) function updateLiveData() { if (AppState.currentSection === 'dashboard') { // Update metrics with small random changes updateDashboardMetrics(); } } // Update dashboard metrics with random changes function updateDashboardMetrics() { const metrics = { 'total-pnl': { base: 42580, range: 500 }, 'max-drawdown': { base: 8420, range: 200 }, 'win-rate': { base: 64.8, range: 1.5 } }; for (const [id, config] of Object.entries(metrics)) { const element = document.getElementById(id); if (element) { const current = parseFloat(element.textContent.replace(/[^\d.-]/g, '')); const change = (Math.random() - 0.5) * 2 * config.range; const newValue = config.base + change; // Format based on metric type if (id === 'total-pnl' || id === 'max-drawdown') { element.textContent = `$${Math.round(newValue).toLocaleString()}`; } else if (id === 'win-rate') { element.textContent = `${newValue.toFixed(1)}%`; } // Add subtle animation element.classList.add('text-highlight'); setTimeout(() => { element.classList.remove('text-highlight'); }, 1000); } } } function updateDashboardWithRealData(data) { // Update key metrics cards const metrics = data.metrics; // Total P&L const pnlEl = document.querySelector('.card-title.text-success'); if (pnlEl) pnlEl.textContent = `$${metrics.total_pnl.toLocaleString()}`; // Max Drawdown const ddEl = document.querySelector('.card-title.text-danger'); if (ddEl) ddEl.textContent = `$${metrics.max_drawdown.toLocaleString()}`; // Win Rate const winEl = document.querySelector('.card-title.text-info'); if (winEl) winEl.textContent = `${metrics.win_rate}%`; // Update chart if (AppState.charts.portfolioChart) { AppState.charts.portfolioChart.data.labels = data.daily_pnl.map(d => d.date); AppState.charts.portfolioChart.data.datasets[0].data = data.daily_pnl.map(d => d.cumulative); AppState.charts.portfolioChart.update(); } } // Show toast notification function showToast(message, type = 'info') { // Create toast container if it doesn't exist let toastContainer = document.getElementById('toast-container'); if (!toastContainer) { toastContainer = document.createElement('div'); toastContainer.id = 'toast-container'; toastContainer.className = 'toast-container position-fixed bottom-0 end-0 p-3'; document.body.appendChild(toastContainer); } // Create toast const toastId = 'toast-' + Date.now(); const toast = document.createElement('div'); toast.id = toastId; toast.className = `toast align-items-center text-bg-${type} border-0`; toast.setAttribute('role', 'alert'); toast.setAttribute('aria-live', 'assertive'); toast.setAttribute('aria-atomic', 'true'); toast.innerHTML = `