AI-Powered Market Making Strategy Simulator
Simulate a market-making strategy and analyze its performance under various market conditions with AI insights.
Simulation Parameters
Higher value means more price swings.
Positive for uptrend, negative for downtrend.
Simulation Results
Key Performance Metrics
| Metric | Value |
|---|---|
| Total P&L | $0.00 |
| Realized P&L | $0.00 |
| Unrealized P&L | $0.00 |
| Max Drawdown | $0.00 |
| Sharpe Ratio (Annualized) | 0.00 |
| Final Capital | $0.00 |
Cumulative P&L Over Time
AI-Powered Insights
Generating AI insights...
`; const initialCapital = parseFloat(initialCapitalInput.value); const bidAskSpreadPct = parseFloat(bidAskSpreadInput.value); const orderSize = parseInt(orderSizeInput.value); const simulationDuration = parseInt(simulationDurationInput.value); const marketVolatility = parseFloat(marketVolatilityInput.value); const marketTrend = parseFloat(marketTrendInput.value); // Input Validation if (isNaN(initialCapital) || initialCapital <= 0) { showMessageBox("Input Error", "Please enter a valid positive initial capital."); resetButtonState(runSimulationBtn, runSimulationBtnText, runSimulationSpinner); return; } if (isNaN(bidAskSpreadPct) || bidAskSpreadPct <= 0) { showMessageBox("Input Error", "Please enter a valid positive bid-ask spread percentage."); resetButtonState(runSimulationBtn, runSimulationBtnText, runSimulationSpinner); return; } if (isNaN(orderSize) || orderSize <= 0) { showMessageBox("Input Error", "Please enter a valid positive order size."); resetButtonState(runSimulationBtn, runSimulationBtnText, runSimulationSpinner); return; } if (isNaN(simulationDuration) || simulationDuration < 10) { showMessageBox("Input Error", "Please enter a simulation duration of at least 10 days."); resetButtonState(runSimulationBtn, runSimulationBtnText, runSimulationSpinner); return; } if (isNaN(marketVolatility) || marketVolatility <= 0) { showMessageBox("Input Error", "Please enter a valid positive market volatility."); resetButtonState(runSimulationBtn, runSimulationBtnText, runSimulationSpinner); return; } if (initialCapital < orderSize * 100) { // Assuming initial price around 100 for a rough check showMessageBox("Capital Warning", "Initial capital might be too low for the chosen order size and market price. Consider increasing capital or reducing order size."); } const initialAssetPrice = 100; // Start asset price at $100 for simulation simplicity const simulatedPrices = generateSimulatedPrices(initialAssetPrice, simulationDuration, marketVolatility, marketTrend); const simulationResults = simulateMarketMaking( initialCapital, bidAskSpreadPct, orderSize, simulatedPrices ); displayPerformanceMetrics(simulationResults); displayPnlTrendChart(simulationResults.pnlHistory, simulationDuration); const resultsSectionElement = document.getElementById('results-section'); if (resultsSectionElement) { resultsSectionElement.classList.remove('hidden'); resultsSectionElement.scrollIntoView({ behavior: 'smooth' }); } if (downloadPdfBtn) downloadPdfBtn.disabled = false; // Generate AI insights await getAIAnalysis(simulationResults, { initialCapital, bidAskSpreadPct, orderSize, simulationDuration, marketVolatility, marketTrend, initialAssetPrice }); resetButtonState(runSimulationBtn, runSimulationBtnText, runSimulationSpinner); }; // --- Display Results --- /** * Populates the performance metrics table. */ function displayPerformanceMetrics(results) { document.getElementById('total-pnl').textContent = `$${results.totalPnl.toFixed(2)}`; document.getElementById('realized-pnl').textContent = `$${results.realizedPnl.toFixed(2)}`; // Unrealized P&L can be derived from final capital - realized P&L - initial capital if inventory is zero. // Otherwise, it's (current price - avg cost) * inventory. For simplicity, we just use the calculated value. document.getElementById('unrealized-pnl').textContent = `$${(results.finalCapital - results.realizedPnl - results.initialCapital).toFixed(2)}`; document.getElementById('max-drawdown').textContent = `$${results.maxDrawdown.toFixed(2)}`; document.getElementById('sharpe-ratio').textContent = results.sharpeRatio.toFixed(2); document.getElementById('final-capital').textContent = `$${results.finalCapital.toFixed(2)}`; } /** * Displays the cumulative P&L trend chart. */ function displayPnlTrendChart(pnlHistory, durationDays) { const ctx = pnlTrendChartCanvas?.getContext('2d'); if (!ctx) return; if (pnlChartInstance) { pnlChartInstance.destroy(); } const labels = Array.from({ length: durationDays }, (_, i) => `Day ${i + 1}`); pnlChartInstance = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: 'Cumulative P&L ($)', data: pnlHistory, borderColor: 'rgb(79, 70, 229)', // Indigo backgroundColor: 'rgba(79, 70, 229, 0.2)', fill: true, tension: 0.1, pointRadius: 0 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { tooltip: { callbacks: { label: function(context) { let label = context.dataset.label || ''; if (label) { label += ': '; } if (context.parsed.y !== null) { label += '$' + context.parsed.y.toFixed(2); } return label; } } } }, scales: { x: { title: { display: true, text: 'Simulation Day' } }, y: { title: { display: true, text: 'Cumulative P&L ($)' }, beginAtZero: true // P&L can start at zero } } } }); } // --- AI Integration --- /** * Calls the Gemini API to get AI-powered insights on the simulation results. * @param {Object} results - Simulation results object. * @param {Object} params - Simulation parameters. */ const getAIAnalysis = async (results, params) => { const aiContainer = document.getElementById('ai-insights-content'); if (!aiContainer) return; aiContainer.innerHTML = `Generating AI insights...
`; const prompt = ` You are a financial analyst specializing in quantitative trading strategies. A user ran a market-making simulation with the following parameters: - Initial Capital: $${params.initialCapital.toLocaleString()} - Bid-Ask Spread: ${params.bidAskSpreadPct}% - Order Size: ${params.orderSize} units - Simulation Duration: ${params.simulationDuration} days - Market Volatility (Daily): ${params.marketVolatility}% - Market Trend (Daily): ${params.marketTrend}% - Initial Asset Price: $${params.initialAssetPrice} Here are the key simulation results: - Total P&L: $${results.totalPnl.toFixed(2)} - Realized P&L: $${results.realizedPnl.toFixed(2)} - Unrealized P&L: $${(results.finalCapital - results.realizedPnl - params.initialCapital).toFixed(2)} - Max Drawdown: $${results.maxDrawdown.toFixed(2)} - Sharpe Ratio (Annualized): ${results.sharpeRatio.toFixed(2)} - Final Capital: $${results.finalCapital.toFixed(2)} Provide a brief, actionable analysis (around 300-400 words) covering: 1. Strategy Performance: Comment on the overall profitability (or loss), Sharpe Ratio, and drawdown, explaining what these metrics indicate. 2. Impact of Parameters: Discuss how the chosen parameters (e.g., spread, volatility, trend) likely influenced the results. For example, a wider spread typically means higher potential profit per trade but fewer fills, while high volatility can create more opportunities but also higher risk. 3. Potential Improvements/Risks: Suggest adjustments to the strategy parameters or conditions that could be tested for better performance, and highlight key risks of market making (e.g., adverse selection, inventory risk). 4. Caveats: Remind the user that this is a simplified simulation and real-world market making is more complex. Format the response in simple HTML using only for titles and - for lists.
`;
try {
const payload = { contents: [{ role: "user", parts: [{ text: prompt }] }] };
const apiKey = ""; // API key is handled by the Canvas environment at runtime
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;
const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
const result = await response.json();
let text = "
Could not generate AI analysis. Please try again.
";
if (result.candidates && result.candidates[0] && result.candidates[0].content && result.candidates[0].content.parts[0]) {
text = result.candidates[0].content.parts[0].text;
}
aiContainer.innerHTML = text;
} catch (error) {
console.error("Error fetching AI insights:", error);
aiContainer.innerHTML = `Error generating AI insights.
`;
}
};
// --- PDF Export Logic ---
/**
* Exports the visible analysis summary (performance table, chart, and AI insights) to a PDF document.
*/
window.downloadPDF = async () => { // Make it global for onclick attribute
const btn = document.getElementById('pdf-button-container')?.querySelector('button');
if (!btn) return;
btn.disabled = true;
btn.textContent = 'Generating PDF...';
const content = document.getElementById('summary-for-pdf');
if (!content) {
showMessageBox("PDF Error", "Content for PDF not found.");
btn.disabled = false;
btn.textContent = 'Download Analysis as PDF';
return;
}
try {
// Temporarily adjust chart and table sizes for better PDF rendering
const chartCanvas = pnlTrendChartCanvas;
const originalChartWidth = chartCanvas.style.width;
const originalChartHeight = chartCanvas.style.height;
chartCanvas.style.width = '1000px'; // Larger size for better quality capture
chartCanvas.style.height = '500px';
if (pnlChartInstance) {
pnlChartInstance.resize(); // Trigger Chart.js to redraw with new dimensions
}
const summaryTable = performanceSummaryTable;
const originalTableWidth = summaryTable.style.width;
summaryTable.style.width = 'fit-content'; // Allow table to expand naturally for capture
const canvas = await html2canvas(content, {
scale: 2, // Increase scale for better resolution
useCORS: true,
logging: false,
backgroundColor: '#ffffff' // Ensure the background is white for PDF
});
// Restore original chart and table sizes
chartCanvas.style.width = originalChartWidth;
chartCanvas.style.height = originalChartHeight;
if (pnlChartInstance) {
pnlChartInstance.resize();
}
summaryTable.style.width = originalTableWidth;
const imgData = canvas.toDataURL('image/png');
const { jsPDF } = window.jspdf;
// Determine orientation based on content width. If the captured canvas is very wide, use landscape.
const orientation = canvas.width > canvas.height * 1.5 ? 'landscape' : 'portrait';
const pdf = new jsPDF({ orientation: orientation, unit: 'mm', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const imgWidth = pdfWidth - 20; // 10mm margin on each side
let imgHeight = (canvas.height * imgWidth) / canvas.width;
let finalImgWidth = imgWidth;
let finalImgHeight = imgHeight;
if (finalImgHeight > pdfHeight - 20) { // If image is taller than page height
finalImgHeight = pdfHeight - 20;
finalImgWidth = (canvas.width * finalImgHeight) / canvas.height;
}
const x = (pdfWidth - finalImgWidth) / 2;
const y = (pdfHeight - finalImgHeight) / 2;
pdf.addImage(imgData, 'PNG', x, y, finalImgWidth, finalImgHeight);
pdf.save(`Market_Making_Simulation_Analysis_${new Date().toISOString().slice(0, 10)}.pdf`);
showMessageBox("PDF Generated", "Your Market Making Strategy Simulation analysis PDF has been successfully downloaded.");
} catch(e) {
console.error("PDF Generation Error: ", e);
showMessageBox("PDF Error", "An error occurred while generating the PDF. Please ensure all content is visible and try again.");
} finally {
btn.disabled = false;
btn.textContent = 'Download Analysis as PDF';
}
};
// --- Initial Setup ---
// No initial simulation run, user explicitly clicks.
});
Could not generate AI analysis. Please try again.
"; if (result.candidates && result.candidates[0] && result.candidates[0].content && result.candidates[0].content.parts[0]) { text = result.candidates[0].content.parts[0].text; } aiContainer.innerHTML = text; } catch (error) { console.error("Error fetching AI insights:", error); aiContainer.innerHTML = `Error generating AI insights.
`; } }; // --- PDF Export Logic --- /** * Exports the visible analysis summary (performance table, chart, and AI insights) to a PDF document. */ window.downloadPDF = async () => { // Make it global for onclick attribute const btn = document.getElementById('pdf-button-container')?.querySelector('button'); if (!btn) return; btn.disabled = true; btn.textContent = 'Generating PDF...'; const content = document.getElementById('summary-for-pdf'); if (!content) { showMessageBox("PDF Error", "Content for PDF not found."); btn.disabled = false; btn.textContent = 'Download Analysis as PDF'; return; } try { // Temporarily adjust chart and table sizes for better PDF rendering const chartCanvas = pnlTrendChartCanvas; const originalChartWidth = chartCanvas.style.width; const originalChartHeight = chartCanvas.style.height; chartCanvas.style.width = '1000px'; // Larger size for better quality capture chartCanvas.style.height = '500px'; if (pnlChartInstance) { pnlChartInstance.resize(); // Trigger Chart.js to redraw with new dimensions } const summaryTable = performanceSummaryTable; const originalTableWidth = summaryTable.style.width; summaryTable.style.width = 'fit-content'; // Allow table to expand naturally for capture const canvas = await html2canvas(content, { scale: 2, // Increase scale for better resolution useCORS: true, logging: false, backgroundColor: '#ffffff' // Ensure the background is white for PDF }); // Restore original chart and table sizes chartCanvas.style.width = originalChartWidth; chartCanvas.style.height = originalChartHeight; if (pnlChartInstance) { pnlChartInstance.resize(); } summaryTable.style.width = originalTableWidth; const imgData = canvas.toDataURL('image/png'); const { jsPDF } = window.jspdf; // Determine orientation based on content width. If the captured canvas is very wide, use landscape. const orientation = canvas.width > canvas.height * 1.5 ? 'landscape' : 'portrait'; const pdf = new jsPDF({ orientation: orientation, unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const imgWidth = pdfWidth - 20; // 10mm margin on each side let imgHeight = (canvas.height * imgWidth) / canvas.width; let finalImgWidth = imgWidth; let finalImgHeight = imgHeight; if (finalImgHeight > pdfHeight - 20) { // If image is taller than page height finalImgHeight = pdfHeight - 20; finalImgWidth = (canvas.width * finalImgHeight) / canvas.height; } const x = (pdfWidth - finalImgWidth) / 2; const y = (pdfHeight - finalImgHeight) / 2; pdf.addImage(imgData, 'PNG', x, y, finalImgWidth, finalImgHeight); pdf.save(`Market_Making_Simulation_Analysis_${new Date().toISOString().slice(0, 10)}.pdf`); showMessageBox("PDF Generated", "Your Market Making Strategy Simulation analysis PDF has been successfully downloaded."); } catch(e) { console.error("PDF Generation Error: ", e); showMessageBox("PDF Error", "An error occurred while generating the PDF. Please ensure all content is visible and try again."); } finally { btn.disabled = false; btn.textContent = 'Download Analysis as PDF'; } }; // --- Initial Setup --- // No initial simulation run, user explicitly clicks. });