const mainChart = document.getElementById("main-chart"); // Portfolio NAV (from Flask → index.html) const NAV = NAV_TS; const buyDate = document.getElementById("sim-date"); const ticker = document.getElementById("sim-ticker-select"); const amount = document.getElementById("sim-cash"); const sim_ticker_select = document.getElementById("sim-ticker-select"); const sim_ticker = document.getElementById("sim-ticker"); const preview_select = document.getElementById("preview-select"); const preview_filter = document.getElementById("preview-filter"); const SIM_METRICS = SIM_METRICS_TS; const SIM_NAV = SIM_NAV_TS; const data = NAV_TS; const layout = { paper_bgcolor: "#3A3A3A", plot_bgcolor: "#3A3A3A", font: { color: "white" }, margin: { t: 40, r: 30, l: 60, b: 60 }, // Increased margins to make room for titles xaxis: { title: { text: "Date", font: { size: 14, color: "#adb5bd" } }, gridcolor: "#666", zeroline: false }, yaxis: { title: { text: "Net Asset Value (USD)", font: { size: 14, color: "#adb5bd" } }, gridcolor: "#666", zeroline: false }, yaxis2: { title: { text: "Asset Preview", font: { size: 14, color: "#adb5bd" } }, overlaying: "y", side: "right", gridcolor: "#444", zeroline: false, }, hovermode: "x unified" }; Plotly.newPlot(mainChart, data, layout, { responsive: true }); let simulatedTraceIndex = null; let simulationActive = false; const portfolioUpload = document.getElementById('portfolioUpload'); if (portfolioUpload){ portfolioUpload.addEventListener('change', function(e) { const file = e.target.files[0]; if (!file) return; const statusDiv = document.getElementById('uploadStatus'); statusDiv.innerHTML = 'Uploading...'; const formData = new FormData(); formData.append('file', file); axios.post('/upload_portfolio', formData, { headers: { 'Content-Type': 'multipart/form-data' } }) .then(response => { statusDiv.innerHTML = 'Upload successful!'; setTimeout(() => window.location.reload(), 1500); }) .catch(error => { console.error(error); statusDiv.innerHTML = 'Upload failed.'; }); }); } const removePortfolioButton = document.getElementById('removePortfolioButton') if (removePortfolioButton){ removePortfolioButton.addEventListener('click', function () { const select = document.getElementById('removePortfolioSelection'); const key = select.value; if (!key) return; if (!confirm('Are you sure you want to remove this portfolio?')) { return; } const statusDiv = document.getElementById('removeStatus'); statusDiv.innerHTML = 'Removing...'; const formData = new FormData(); formData.append('portfolio', key); axios.post('/remove_portfolio', formData) .then(response => { statusDiv.innerHTML = 'Removed!'; setTimeout(() => window.location.reload(), 1500); }) .catch(error => { console.error(error); statusDiv.innerHTML = 'Removal failed.'; }); }); } function drawSimulationLine(nav) { const trace = { x: nav.map(x => x.date), y: nav.map(x => x.nav), name: "Simulation", line: { width: 3, color: "#f5c542" } }; if (simulatedTraceIndex === null) { Plotly.addTraces(mainChart, trace); simulatedTraceIndex = mainChart.data.length - 1; } else { Plotly.restyle(mainChart, { x: [trace.x], y: [trace.y] }, [simulatedTraceIndex]); } simulationActive = true; } function clearSimulationLine() { if (simulatedTraceIndex !== null) { Plotly.deleteTraces(mainChart, simulatedTraceIndex); simulatedTraceIndex = null; } simulationActive = false; } function showSimulationMetrics(metrics) { for (const key in metrics) { const el = document.getElementById("kpi-" + key.replace("_", "-")); if (!el) continue; const simLine = el.querySelector(".metric-sim"); simLine.innerText = metrics[key]; simLine.classList.remove("d-none"); } } function clearSimulationMetrics() { document.querySelectorAll(".metric-sim").forEach(el => { el.innerText = ""; el.classList.add("d-none"); }); } async function simulate() { if (!ticker.value || !buyDate.value || !amount.value) { alert("Please enter date, ticker and cash amount"); return; } const res = await fetch("/api/simulate/purchase", { method: "POST", headers: {"Content-Type":"application/json"}, body: JSON.stringify({ date: buyDate.value, ticker: ticker.value.toUpperCase(), cash: amount.value }) }); if (!res.ok) { const err = await res.text(); alert(err); return; } window.location.reload(); } async function undo() { await fetch("/api/simulate/reset", { method:"POST" }); window.location.reload(); } if (SIM_METRICS && SIM_NAV) { drawSimulationLine(SIM_NAV); showSimulationMetrics(SIM_METRICS); } function queryFilterHandler(e, selector) { const val = e.target.value.toLowerCase().trim(); const entries = Object.entries(TICKER_TO_NAME).filter(([t, name]) => !val || t.toLowerCase().includes(val) || name.toLowerCase().includes(val) ); selector.innerHTML = ""; for (const [ticker, name] of entries) { const option = document.createElement("option"); option.value = ticker; option.innerHTML = name; selector.appendChild(option); } } function setCookie(name, value, days = 7) { const expires = new Date(Date.now() + days * 864e5).toUTCString(); document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/; SameSite=Lax`; } function getCookie(name) { return document.cookie .split("; ") .find(row => row.startsWith(name + "=")) ?.split("=")[1]; } function deleteCookie(name) { document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`; } async function showPreview() { const ticker = preview_select.value; //await cookieStore.set("preview", ticker); await setCookie("preview", ticker); window.location.reload(); } async function clearPreview() { await deleteCookie("preview"); window.location.reload(); } preview_filter.addEventListener("input", (e) => queryFilterHandler(e, preview_select)); sim_ticker.addEventListener("input", (e) => queryFilterHandler(e, sim_ticker_select)); if (PREVIEW_DATA) { Plotly.addTraces(mainChart, PREVIEW_DATA); }