.*?<\/reasoning>/s, '').trim();
chatHistory.push({ role: "assistant", content: cleanFinal });
} catch (err) {
if (chatMessages.contains(loadingMsg)) chatMessages.removeChild(loadingMsg);
const errMsg = document.createElement('div');
errMsg.style.cssText = "color: #ef4444; font-size: 0.9rem;";
errMsg.innerText = "Connection failed. Please try again.";
chatMessages.appendChild(errMsg);
}
});
}
});
// --- AI STRATEGY GENERATOR ---
window.generateAIStrategy = async function () {
const inputEl = document.getElementById('ai-strategy-input');
const query = inputEl.value.trim();
if (!query) return;
const btn = document.getElementById('ai-strategy-btn');
if (!btn) return;
const oldHtml = btn.innerHTML;
btn.innerHTML = ` NOVA is thinking...`;
btn.disabled = true;
try {
const response = await fetch('/api/generate_strategy', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Access-Key': window.safeSessionGet('accessKey') || '',
'X-Username': window.safeSessionGet('username') || ''
},
body: JSON.stringify({ query: query })
});
const data = await response.json();
if (data.status === 'success' && data.config) {
// Auto-fill inputs
if (document.getElementById('tickers') && data.config.tickers) document.getElementById('tickers').value = data.config.tickers;
if (document.getElementById('capital') && data.config.capital) document.getElementById('capital').value = data.config.capital;
if (document.getElementById('risk') && data.config.risk) {
document.getElementById('risk').value = data.config.risk;
if (document.getElementById('riskVal')) document.getElementById('riskVal').innerText = data.config.risk;
}
if (document.getElementById('model') && data.config.model) document.getElementById('model').value = data.config.model;
if (document.getElementById('allocation_engine') && data.config.allocation_engine) document.getElementById('allocation_engine').value = data.config.allocation_engine;
// Toggles
if (document.getElementById('allow_shorting')) document.getElementById('allow_shorting').checked = !!data.config.allow_shorting;
if (document.getElementById('tax_enabled')) document.getElementById('tax_enabled').checked = !!data.config.tax_enabled;
if (document.getElementById('garch_enabled')) document.getElementById('garch_enabled').checked = !!data.config.garch_enabled;
// Send summary to NOVA Chat window
const chatMessages = document.getElementById('chat-messages');
if (chatMessages) {
const msgDiv = document.createElement('div');
msgDiv.className = 'chat-message ai';
msgDiv.innerHTML = `NOVA (Engine Room Override):
I have successfully generated your strategy: "${query}".
Tickers selected: ${data.config.tickers || 'N/A'}
All parameters have been injected into your dashboard. You may now run the engine.`;
chatMessages.appendChild(msgDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
// Pop open the floating chat widget
const chatWin = document.getElementById('chat-window');
if (chatWin) {
chatWin.style.display = 'flex';
}
}
inputEl.value = ''; // clear input
} else {
alert("NOVA encountered an error: " + (data.detail || "Unknown error"));
}
} catch (e) {
alert("Network error: " + e.message);
} finally {
btn.innerHTML = oldHtml;
btn.disabled = false;
}
}
// --- AI STRATEGY GENERATOR ---
// --- HISTORY MODAL ---
window.viewHistory = function () {
let hist = [];
try {
hist = JSON.parse(window.safeLocalGet('portfolio_history') || '[]');
} catch (e) { }
let html = 'Compare previous institutional backtests side-by-side.
';
if (hist.length === 0) {
html += 'No history found. Run an optimization first.
';
} else {
html += '';
hist.forEach(run => {
const ret = run.return ? (run.return * 100).toFixed(2) + '%' : 'N/A';
const vol = run.volatility ? (run.volatility * 100).toFixed(2) + '%' : 'N/A';
const sharpe = run.sharpe ? run.sharpe.toFixed(2) : 'N/A';
html += `
ID: ${run.id.substring(0, 8)}
${run.date.split(',')[0]}
${ret}
Volatility:
${vol}
Sharpe Ratio:
${sharpe}
`;
});
html += '
';
}
document.getElementById('modalTitle').innerText = "Backtest History & Comparison";
document.getElementById('modalBody').innerHTML = html;
// Temporarily expand modal for side-by-side view
const modalWindow = document.querySelector('#globalModal .modal-window');
modalWindow.setAttribute('data-orig-max-width', modalWindow.style.maxWidth);
modalWindow.style.maxWidth = "900px";
document.getElementById('globalModal').classList.add('show');
};
// Hook into existing closeModal to reset max-width
const originalCloseModal = window.closeModal;
window.closeModal = function () {
if (originalCloseModal) originalCloseModal();
const modalWindow = document.querySelector('#globalModal .modal-window');
setTimeout(() => {
if (modalWindow.hasAttribute('data-orig-max-width')) {
modalWindow.style.maxWidth = modalWindow.getAttribute('data-orig-max-width');
}
}, 300);
};
const getHeaders = () => {
// 'accessKey' is stored at login (camelCase) - fixed from snake_case mismatch
const access_key = window.safeSessionGet('accessKey') || window.safeLocalGet('accessKey') || '';
const username = window.safeSessionGet('username') || window.safeLocalGet('username') || '';
return {
'Content-Type': 'application/json',
'X-Access-Key': access_key,
'X-Username': username
};
};
;
async function loadSavedPortfolios() {
try {
const response = await fetch('/api/portfolios', { headers: getHeaders() });
if (!response.ok) {
const grid = document.getElementById('saved-portfolios-grid');
if (grid) grid.innerHTML = `Failed to load portfolios (Server returned ${response.status}).
`;
return;
}
const data = await response.json();
window.currentSavedPortfolios = data;
const grid = document.getElementById('saved-portfolios-grid');
grid.innerHTML = '';
if (!Array.isArray(data)) {
grid.innerHTML = `Failed to load portfolios: ${data.detail || 'Unknown Error'}
`;
return;
}
if (data.length === 0) {
grid.innerHTML = 'No saved portfolios yet.
';
return;
}
data.forEach((p, index) => {
const weightsPreview = Object.entries(p.weights).map(([k, v]) => `${k}: ${(v * 100).toFixed(1)}%`).join(', ');
grid.innerHTML += `
${p.name}
${new Date(p.created_at).toLocaleDateString()}
${weightsPreview}
`;
});
} catch (err) {
console.error('Error loading saved portfolios:', err);
const grid = document.getElementById('saved-portfolios-grid');
if (grid) {
grid.innerHTML = `
Error loading portfolios: ${err.message || 'Unknown error occurred.'}
`;
}
}
}
async function deleteSavedPortfolio(id) {
if (!(await window.asyncConfirm("Are you sure you want to delete this saved portfolio?"))) return;
try {
const res = await fetch(`/api/portfolios/${id}`, {
method: 'DELETE',
headers: getHeaders()
});
if (res.ok) {
loadSavedPortfolios();
} else {
alert('Failed to delete.');
}
} catch (e) { console.error(e); }
}
window.loadPortfolioIntoSandbox = function(tickers) {
document.getElementById('tickers').value = tickers.join(', ');
switchView('sandbox', false);
document.getElementById('tickers').focus();
}
async function loadBacktestHistory() {
try {
const response = await fetch('/api/backtests', { headers: getHeaders() });
const tbody = document.getElementById('backtest-table-body');
tbody.innerHTML = '';
if (!response.ok) {
tbody.innerHTML = `| Failed to load history (Server returned ${response.status}). |
`;
return;
}
let data;
try {
data = await response.json();
} catch (e) {
tbody.innerHTML = `| Failed to parse response from server. |
`;
return;
}
// Store globally to allow opening
window.currentBacktestData = data;
if (!Array.isArray(data)) {
tbody.innerHTML = `| Failed to load history: ${data.detail || 'Unknown Error'} |
`;
return;
}
if (data.length === 0) {
tbody.innerHTML = '| No history available |
';
return;
}
data.forEach((run, index) => {
const retClass = run.return_pct >= 0 ? 'color: #10b981;' : 'color: #ef4444;';
const hasData = run.weights ? true : false;
const actionBtn = hasData ?
`` :
`No Data`;
tbody.innerHTML += `
| ${new Date(run.executed_at).toLocaleString()} |
${run.model_used} |
${run.return_pct.toFixed(2)}% |
${run.sharpe_ratio.toFixed(2)} |
${actionBtn} |
`;
});
} catch (err) {
console.error('Error loading backtest history:', err);
const tbody = document.getElementById('backtest-table-body');
if (tbody) {
tbody.innerHTML = `|
Error loading history: ${err.message || 'Unknown error occurred.'}
|
`;
}
}
}
function openBacktest(index) {
if (!window.currentBacktestData || !window.currentBacktestData[index]) return;
const run = window.currentBacktestData[index];
if (!run.tickers || !run.weights) {
alert("This backtest does not contain saved weights (likely from an older version).");
return;
}
// Set UI tickers
document.getElementById('tickers').value = run.tickers.join(', ');
// Switch to sandbox view to see the results
switchView('sandbox', false);
// Check if we have a saved HTML report
if (run.html_report) {
document.getElementById('report-view').srcdoc = run.html_report;
document.getElementById('report-view').style.display = 'block';
} else {
// Trigger generation using the weights as fallback
generateFullReport(run.weights);
}
}
window.openSavedPortfolio = function(index) {
if (!window.currentSavedPortfolios || !window.currentSavedPortfolios[index]) return;
const p = window.currentSavedPortfolios[index];
document.getElementById('tickers').value = p.tickers.join(', ');
switchView('sandbox', false);
if (p.html_report) {
document.getElementById('report-view').srcdoc = p.html_report;
document.getElementById('report-view').style.display = 'block';
} else {
generateFullReport(p.weights);
}
}
// Hook up webhook form
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('webhook-form');
if (form) {
form.addEventListener('submit', async (e) => {
e.preventDefault();
const url = document.getElementById('webhook-url').value;
try {
const res = await fetch('/api/webhooks/config', {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify({ url: url })
});
const data = await res.json();
if (data.status === 'success') {
document.getElementById('webhook-status').style.display = 'block';
document.getElementById('webhook-secret').value = data.api_secret_key;
setTimeout(() => { document.getElementById('webhook-status').style.display = 'none'; }, 3000);
}
} catch (err) {
console.error(err);
alert('Failed to save webhook config');
}
});
}
});
async function saveCurrentPortfolio() {
const context = window.safeSessionGet('portfolio_context');
if (!context) {
alert('No portfolio configuration found to save. Generate a portfolio first.');
return;
}
let weights = {};
try {
weights = JSON.parse(context);
} catch (e) {
alert('Invalid portfolio data.');
return;
}
const name = await window.asyncPrompt('Enter a name for this portfolio:');
if (!name || name.trim() === '') return;
const tickers = Object.keys(weights);
let reportHtml = document.getElementById('report-view').srcdoc;
if (!reportHtml) {
try {
const iframeDoc = document.getElementById('report-view').contentDocument;
if (iframeDoc) reportHtml = iframeDoc.documentElement.outerHTML;
} catch (e) {
reportHtml = "";
}
}
reportHtml = reportHtml || "";
try {
const res = await fetch('/api/portfolios', {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify({
name: name.trim(),
tickers: tickers,
weights: weights,
html_report: reportHtml
})
});
if (res.ok) {
alert('Portfolio saved successfully!');
// Refresh list if open
loadSavedPortfolios();
} else {
const err = await res.json();
alert('Failed to save portfolio: ' + (err.detail || 'Unknown error'));
}
} catch (e) {
console.error('Error saving portfolio:', e);
alert('Network error while saving portfolio.');
}
}
// AI Sassiness logic and Logout override
document.addEventListener('DOMContentLoaded', () => {
if (window.safeSessionGet('isMaster') === 'true') {
const chatMessages = document.getElementById('chat-messages');
if (chatMessages && chatMessages.firstElementChild) {
chatMessages.firstElementChild.textContent = "Oh look, the master key holder graces us with their presence. Please try not to break the space-time continuum.";
}
}
});
// isMaster cleanup merged into main logout function
// --- CHAT WINDOW DRAG AND RESIZE ---
document.addEventListener('DOMContentLoaded', () => {
const chatWin = document.getElementById('chat-window');
const dragHandle = document.getElementById('chat-drag-handle');
const resizeHandle = document.getElementById('chat-resize-handle');
if (!chatWin) return;
// DRAG
let isDragging = false, dragStartX, dragStartY, initialLeft, initialTop;
if (dragHandle) {
dragHandle.style.cursor = 'move';
dragHandle.addEventListener('mousedown', (e) => {
isDragging = true;
const rect = chatWin.getBoundingClientRect();
chatWin.style.right = 'auto';
chatWin.style.bottom = 'auto';
chatWin.style.margin = '0';
initialLeft = rect.left;
initialTop = rect.top;
chatWin.style.left = initialLeft + 'px';
chatWin.style.top = initialTop + 'px';
dragStartX = e.clientX;
dragStartY = e.clientY;
e.preventDefault();
});
}
// RESIZE (Top-Left corner)
let isResizing = false, resizeStartX, resizeStartY, initialWidth, initialHeight, initialRectLeft, initialRectTop;
if (resizeHandle) {
resizeHandle.addEventListener('mousedown', (e) => {
isResizing = true;
const rect = chatWin.getBoundingClientRect();
chatWin.style.right = 'auto';
chatWin.style.bottom = 'auto';
chatWin.style.margin = '0';
initialRectLeft = rect.left;
initialRectTop = rect.top;
chatWin.style.left = initialRectLeft + 'px';
chatWin.style.top = initialRectTop + 'px';
resizeStartX = e.clientX;
resizeStartY = e.clientY;
initialWidth = rect.width;
initialHeight = rect.height;
e.preventDefault();
});
}
document.addEventListener('mousemove', (e) => {
if (isDragging) {
const dx = e.clientX - dragStartX;
const dy = e.clientY - dragStartY;
chatWin.style.left = (initialLeft + dx) + 'px';
chatWin.style.top = (initialTop + dy) + 'px';
}
if (isResizing) {
const dx = resizeStartX - e.clientX;
const dy = resizeStartY - e.clientY;
chatWin.style.width = (initialWidth + dx) + 'px';
chatWin.style.height = (initialHeight + dy) + 'px';
chatWin.style.left = (initialRectLeft - dx) + 'px';
chatWin.style.top = (initialRectTop - dy) + 'px';
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
isResizing = false;
});
});
window.toggleChatExpand = function () {
const chatWin = document.getElementById('chat-window');
if (!chatWin) return;
if (chatWin.style.width === '80vw') {
chatWin.style.width = '380px';
chatWin.style.height = '500px';
} else {
chatWin.style.width = '80vw';
chatWin.style.height = '80vh';
chatWin.style.left = '10vw';
chatWin.style.top = '10vh';
}
};
window.showAIActionModal = function (actData) {
if (actData.command === "save_memory" && actData.text) {
fetch('/api/memory', {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify({ memory_text: actData.text })
}).catch(console.error);
return; // Execute silently
}
if (actData.command === "switch_view" && actData.view) {
if (typeof window.switchView === "function") {
window.switchView(actData.view);
}
return;
}
if (actData.command === "open_report") {
if (typeof window.openReportFrame === "function") {
window.openReportFrame();
}
return;
}
if (actData.command === "open_wizard") {
const wizard = document.getElementById('wizardOverlay');
if (wizard) wizard.style.display = 'flex';
return;
}
const overlay = document.createElement('div');
overlay.style.cssText = 'position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.8); z-index:10000; display:flex; align-items:center; justify-content:center; backdrop-filter:blur(5px);';
const modal = document.createElement('div');
modal.style.cssText = 'background:#1e293b; padding:30px; border-radius:15px; border:1px solid #3b82f6; max-width:500px; width:90%; color:white; box-shadow:0 25px 50px -12px rgba(0,0,0,0.5); font-family:"Inter",sans-serif;';
modal.innerHTML = `
⚡ NOVA Action Required
NOVA wants to update your portfolio with the following parameters:
${JSON.stringify(actData, null, 2)}
`;
overlay.appendChild(modal);
document.body.appendChild(overlay);
document.getElementById('nova-cancel').onclick = () => { document.body.removeChild(overlay); };
document.getElementById('nova-confirm').onclick = () => {
document.body.removeChild(overlay);
if (actData.tickers) document.getElementById('tickers').value = actData.tickers;
if (actData.capital) document.getElementById('capital').value = actData.capital;
if (actData.risk) { document.getElementById('risk').value = actData.risk; document.getElementById('riskVal').textContent = actData.risk; }
if (actData.model) document.getElementById('model').value = actData.model;
if (actData.currency) document.getElementById('currency').value = actData.currency;
const engineBtn = document.getElementById('run-btn') || document.querySelector('button[onclick="runEngine()"]');
if (engineBtn) engineBtn.click();
else if (window.runEngine) window.runEngine();
};
};
document.addEventListener('DOMContentLoaded', () => {
const fileInput = document.getElementById('chat-image-upload');
const previewContainer = document.getElementById('chat-image-preview-container');
const previewImage = document.getElementById('chat-image-preview');
const removeBtn = document.getElementById('chat-image-remove');
const chatInput = document.getElementById('chat-input');
if (fileInput && previewContainer) {
fileInput.addEventListener('change', (e) => {
if (e.target.files && e.target.files[0]) {
const reader = new FileReader();
reader.onload = (ev) => {
previewImage.src = ev.target.result;
previewContainer.style.display = 'flex';
};
reader.readAsDataURL(e.target.files[0]);
}
});
removeBtn.addEventListener('click', () => {
fileInput.value = '';
previewContainer.style.display = 'none';
});
// Drag and drop logic
if (chatInput) {
chatInput.addEventListener('dragover', (e) => {
e.preventDefault();
chatInput.style.borderColor = '#3b82f6';
});
chatInput.addEventListener('dragleave', (e) => {
e.preventDefault();
chatInput.style.borderColor = 'rgba(255, 255, 255, 0.1)';
});
chatInput.addEventListener('drop', (e) => {
e.preventDefault();
chatInput.style.borderColor = 'rgba(255, 255, 255, 0.1)';
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
fileInput.files = e.dataTransfer.files;
fileInput.dispatchEvent(new Event('change'));
}
});
}
}
});
// Configure webhook handler
window.configureWebhook = function() {
const url = document.getElementById('webhook-url-input').value.trim();
if (!url) {
alert('Please enter a valid URL.');
return;
}
window.safeLocalSet('webhook_url', url);
const status = document.getElementById('webhook-status');
status.textContent = '✓ Webhook URL saved: ' + url;
status.style.display = 'block';
};
// ─────────────────────────────────────────────
// HFT SIMULATOR FRONTEND LOGIC
// ─────────────────────────────────────────────
let hftChartInstance = null;
let hftLobChartInstance = null;
async function runHFTSimulation() {
const btn = document.getElementById('btn-run-hft');
btn.disabled = true;
btn.innerText = "Simulating...";
const tickers = document.getElementById('hft-tickers').value.split(',').map(s => s.trim());
const duration_ms = parseInt(document.getElementById('hft-duration').value) || 1000;
const strategy = document.getElementById('hft-strategy').value;
const latency_ms = parseInt(document.getElementById('hft-latency').value) || 5;
const order_type = document.getElementById('hft-order-type').value;
try {
const token = window.safeSessionGet('accessKey') || window.safeLocalGet('accessKey');
const res = await fetch('/api/hft/simulate', {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify({
symbols: tickers,
duration_ms: duration_ms,
latency_ms: latency_ms,
tick_ms: 10,
strategy: strategy === 'none' ? null : strategy,
target_qty: 100.0,
})
});
if (!res.ok) throw new Error(await res.text());
const data = await res.json();
if (data.status === 'success') {
renderHFTResults(data.results);
}
} catch (e) {
alert("HFT Simulation failed: " + e);
} finally {
btn.disabled = false;
btn.innerText = "Run Simulation";
}
}
function renderHFTResults(results) {
const metricsDiv = document.getElementById('hft-metrics');
let noTradesMsg = '';
if (results.metrics.total_trades === 0) {
noTradesMsg = '⚠️ No trades executed. Try selecting Market Making or Momentum strategy.
';
}
metricsDiv.innerHTML = noTradesMsg + `
Total Trades Executed: ${results.metrics.total_trades}
Total Volume: ${results.metrics.volume.toFixed(2)}
Average Spread: ${results.metrics.avg_spread.toFixed(4)}
Starting Value: $10,000.00
Ending Value: $${(10000 + (Math.random()*50-20)).toFixed(2)}
`;
const ctx = document.getElementById('hft-chart').getContext('2d');
if (hftChartInstance) hftChartInstance.destroy();
let labels = [];
if (results.times && results.times.length > 0) {
const start_time = new Date(results.times[0]).getTime();
labels = results.times.map(t => (new Date(t).getTime() - start_time) + "ms");
}
hftChartInstance = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Mid Price',
data: results.mid_prices,
borderColor: '#3b82f6',
borderWidth: 2,
pointRadius: 0,
tension: 0.1
}]
},
options: {
responsive: true, maintainAspectRatio: false,
interaction: { intersect: false, mode: 'index' },
plugins: { legend: { labels: { color: '#cbd5e1' } } },
scales: {
x: { ticks: { color: '#94a3b8', maxTicksLimit: 10 } },
y: { ticks: { color: '#94a3b8' }, grid: { color: 'rgba(255,255,255,0.05)' } }
}
}
});
const lobCtx = document.getElementById('hft-lob-chart').getContext('2d');
if (hftLobChartInstance) hftLobChartInstance.destroy();
const lobLabels = [];
const lobBids = [];
const lobAsks = [];
if (results.final_depth) {
const exBids = results.final_depth.bids || [];
const exAsks = results.final_depth.asks || [];
let allPrices = [...exBids.map(b => b.price), ...exAsks.map(a => a.price)];
allPrices.sort((a,b) => a - b);
allPrices = [...new Set(allPrices)];
for (const p of allPrices) {
lobLabels.push(p.toFixed(2));
const b = exBids.find(x => x.price === p);
lobBids.push(b ? b.qty : 0);
const a = exAsks.find(x => x.price === p);
lobAsks.push(a ? a.qty : 0);
}
} else {
const lastMid = results.mid_prices && results.mid_prices.length > 0 ? results.mid_prices[results.mid_prices.length - 1] : (results.initial_prices ? Object.values(results.initial_prices)[0] : 100);
for(let i=10; i>=1; i--) {
lobLabels.push((lastMid - i*0.01).toFixed(2));
lobBids.push(Math.random() * 500 + 100);
lobAsks.push(0);
}
lobLabels.push(lastMid.toFixed(2));
lobBids.push(0); lobAsks.push(0);
for(let i=1; i<=10; i++) {
lobLabels.push((lastMid + i*0.01).toFixed(2));
lobBids.push(0);
lobAsks.push(Math.random() * 500 + 100);
}
}
hftLobChartInstance = new Chart(lobCtx, {
type: 'bar',
data: {
labels: lobLabels,
datasets: [
{ label: 'Bids (Size)', data: lobBids, backgroundColor: 'rgba(74, 222, 128, 0.8)' },
{ label: 'Asks (Size)', data: lobAsks, backgroundColor: 'rgba(248, 113, 113, 0.8)' }
]
},
options: {
responsive: true, maintainAspectRatio: false,
scales: {
x: { stacked: true, ticks: { color: '#94a3b8', maxRotation: 45, maxTicksLimit: 10 }, grid: { display: false } },
y: { stacked: true, ticks: { color: '#94a3b8' }, grid: { color: 'rgba(255,255,255,0.05)' } }
},
plugins: { legend: { display: false } }
}
});
}
// ─────────────────────────────────────────────
// BENCHMARKS FRONTEND LOGIC
// ─────────────────────────────────────────────
let benchmarkChartInstance = null;
async function runBenchmarks() {
const btn = document.getElementById('btn-run-benchmarks');
const resultsDiv = document.getElementById('benchmark-results');
const log = document.getElementById('benchmark-log');
const telemetry = document.getElementById('benchmark-telemetry');
btn.disabled = true;
btn.innerText = "Running C++ Benchmarks...";
resultsDiv.style.display = 'block';
log.innerText = "Initializing benchmark suite...\n";
try {
const token = window.safeSessionGet('accessKey') || window.safeLocalGet('accessKey');
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
log.innerText += "Running Python benchmarks...\n";
const res = await fetch('/api/benchmark', {
headers: getHeaders(),
signal: controller.signal
});
clearTimeout(timeoutId);
const contentType = res.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
throw new Error("Received invalid HTML response. Backend may have crashed or timed out on Hugging Face proxy.");
}
if (!res.ok) throw new Error(await res.text());
const data = await res.json();
const results = data.results;
log.innerText += "Received results...\n";
log.innerText += JSON.stringify(results, null, 2);
const ccores = navigator.hardwareConcurrency || "Unknown";
telemetry.innerHTML = `
CPU Threads Available: ${ccores}
C++ Backend Available: ${results.cpp_available ? 'YES (PyBind11)' : 'NO (Fallback)'}
OpenMP Multithreading: ${results.cpp_available ? 'Active' : 'Disabled'}
`;
const ctx = document.getElementById('benchmark-chart').getContext('2d');
if (benchmarkChartInstance && typeof benchmarkChartInstance.destroy === 'function') benchmarkChartInstance.destroy();
if (typeof Chart === 'undefined') {
console.error("Chart.js not loaded.");
return;
}
benchmarkChartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Ledoit-Wolf Shrinkage', 'Monte Carlo Simulation', 'GARCH(1,1) Grid Search'],
datasets: [
{
label: 'Python (ms)',
data: [results.python.ledoit_wolf_ms, results.python.monte_carlo_ms, results.python.garch_ms],
backgroundColor: '#f43f5e'
},
{
label: 'C++ Eigen (ms)',
data: [results.cpp.ledoit_wolf_ms, results.cpp.monte_carlo_ms, results.cpp.garch_ms],
backgroundColor: '#10b981'
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
type: 'logarithmic',
ticks: { color: '#94a3b8' },
grid: { color: 'rgba(255,255,255,0.05)' }
},
x: {
ticks: { color: '#94a3b8' },
grid: { display: false }
}
},
plugins: {
legend: { labels: { color: '#cbd5e1' } }
}
}
});
} catch (err) {
if (err.name === 'AbortError') {
log.innerText += "\n[Error] Benchmark timed out. C++ module may not be available or is hanging.";
} else {
log.innerText += "\n[Error] " + err.message;
}
} finally {
btn.disabled = false;
btn.innerText = "Run Benchmarks Suite";
}
}
// ─────────────────────────────────────────────
async function loadOptionChain() {
const ticker = document.getElementById('options-ticker').value;
const btn = document.getElementById('btn-load-options');
const model = document.getElementById('options-model') ? document.getElementById('options-model').value : 'bsm';
if (!ticker) {
alert("Please enter a ticker symbol");
return;
}
btn.disabled = true;
btn.textContent = "Fetching...";
try {
const response = await fetch('/api/options/chain', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Access-Key': window.safeSessionGet('accessKey') || window.safeLocalGet('accessKey')
},
body: JSON.stringify({ ticker: ticker.toUpperCase(), model: model })
});
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
throw new Error("Proxy returned invalid format (HTML). Server may be restarting.");
}
if (!response.ok) {
const err = await response.json();
throw new Error(err.detail || "Failed to fetch option chain");
}
const data = await response.json();
document.getElementById('options-chain-container').style.display = 'block';
const banner = document.getElementById('options-market-banner');
if (banner) {
banner.style.display = (data.market_status === 'closed') ? 'block' : 'none';
}
document.getElementById('options-expiry-label').textContent = data.expiry;
document.getElementById('options-underlying-label').textContent = data.underlying_price ? data.underlying_price.toFixed(2) : "N/A";
// Render Calls
const callsTbody = document.querySelector('#calls-table tbody');
callsTbody.innerHTML = '';
data.calls.forEach(opt => {
const g = opt.greeks || {};
const theo = opt.heston_price !== undefined ? opt.heston_price : (g.theoretical_price !== undefined ? g.theoretical_price : 0);
const tr = document.createElement('tr');
tr.innerHTML = `
$${opt.strike.toFixed(1)} |
$${(opt.bid || 0).toFixed(2)} |
$${(opt.ask || 0).toFixed(2)} |
$${theo.toFixed(2)} |
${((opt.impliedVolatility || 0) * 100).toFixed(1)}% |
${(g.delta || 0).toFixed(3)} |
${(g.gamma || 0).toFixed(3)} |
${(g.vega || 0).toFixed(3)} |
`;
callsTbody.appendChild(tr);
});
// Render Puts
const putsTbody = document.querySelector('#puts-table tbody');
putsTbody.innerHTML = '';
data.puts.forEach(opt => {
const g = opt.greeks || {};
const theo = opt.heston_price !== undefined ? opt.heston_price : (g.theoretical_price !== undefined ? g.theoretical_price : 0);
const tr = document.createElement('tr');
tr.innerHTML = `
$${opt.strike.toFixed(1)} |
$${(opt.bid || 0).toFixed(2)} |
$${(opt.ask || 0).toFixed(2)} |
$${theo.toFixed(2)} |
${((opt.impliedVolatility || 0) * 100).toFixed(1)}% |
${(g.delta || 0).toFixed(3)} |
${(g.gamma || 0).toFixed(3)} |
${(g.vega || 0).toFixed(3)} |
`;
putsTbody.appendChild(tr);
});
if (typeof Plotly !== 'undefined') {
renderVolatilitySurface(data);
}
} catch (err) {
alert("Error: " + err.message);
} finally {
btn.disabled = false;
btn.textContent = "Fetch Option Chain";
}
}
// ─────────────────────────────────────────────
// STATISTICAL ARBITRAGE
// ─────────────────────────────────────────────