/* * 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 = `
${message}
`; toastContainer.appendChild(toast); // Initialize and show toast const bsToast = new bootstrap.Toast(toast, { delay: 3000 }); bsToast.show(); // Remove toast after it's hidden toast.addEventListener('hidden.bs.toast', function() { toast.remove(); }); } // Load mock data function loadMockData() { // Generate mock data for charts AppState.mockData = { portfolioData: generatePortfolioData(), dataQualityData: generateDataQualityData(), backtestData: generateBacktestData(), riskData: generateRiskData() }; } // Generate mock portfolio data function generatePortfolioData() { const data = []; let value = 100000; for (let i = 0; i < 30; i++) { const date = new Date(); date.setDate(date.getDate() - (30 - i)); // Random walk with drift const change = (Math.random() - 0.4) * 0.02 * value; value += change; data.push({ date: date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }), value: Math.max(90000, value) }); } return data; } // Generate mock data quality data function generateDataQualityData() { const data = []; for (let i = 0; i < 7; i++) { const date = new Date(); date.setDate(date.getDate() - (7 - i)); data.push({ date: date.toLocaleDateString('en-US', { weekday: 'short' }), completeness: 90 + Math.random() * 8, latency: 5 + Math.random() * 15, errors: Math.floor(Math.random() * 5) }); } return data; } // Generate mock backtest data function generateBacktestData() { const strategies = ['Dual Momentum', 'Mean Reversion', 'Pairs Trading']; const data = []; strategies.forEach(strategy => { let returns = 100; const returnsData = []; for (let i = 0; i < 12; i++) { const monthlyReturn = (Math.random() - 0.2) * 0.1; returns *= (1 + monthlyReturn); returnsData.push(returns); } data.push({ strategy, returns: returnsData, sharpe: 0.8 + Math.random() * 1.5, winRate: 50 + Math.random() * 30 }); }); return data; } // Generate mock risk data function generateRiskData() { const metrics = ['VaR', 'CVaR', 'Max Drawdown', 'Volatility']; const data = []; metrics.forEach(metric => { const values = []; for (let i = 0; i < 10; i++) { values.push(Math.random() * 10); } data.push({ metric, values }); }); return data; } // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', initApp); // Export for use in other scripts window.App = { showSection, showToast, refreshSectionCharts, state: AppState };