Algorithmic Trading Backtesting Platform

Strategy & Data Parameters

Note: Price data is simulated for this tool.

Execute Backtest

Once your strategy and data parameters are set up, click the button below to run the backtest. The simulation will use generated historical price data.

Backtest Results & Analysis

Run a backtest to see the results here.

Equity Curve

Trade Log

No trades executed yet.

Final Portfolio Value: ${atbp_formatCurrency(perf.finalPortfolioValue)}

Total Profit/Loss: ${atbp_formatCurrency(perf.totalPL)}

Return on Investment (ROI): ${perf.roiPercentage.toFixed(2)}%

Number of Trades: ${perf.numTrades}

Winning Trades: ${perf.winningTrades}

Losing Trades: ${perf.losingTrades}

Win/Loss Ratio: ${isFinite(perf.winLossRatio) ? perf.winLossRatio.toFixed(2) : 'N/A'}

Max Drawdown: ${perf.maxDrawdownPercentage.toFixed(2)}%

`; // Display Trade Log if (atbp_backtestResults.tradeLog.length > 0) { let tableHtml = ''; atbp_backtestResults.tradeLog.forEach(trade => { tableHtml += ``; }); tableHtml += '
DateTypePrice ($)SharesValue ($)Cash ($)Portfolio ($)P/L ($)
${trade.date} ${trade.type} ${trade.price} ${trade.shares} ${trade.value} ${trade.cash} ${trade.portfolioValue} ${trade.profit !== undefined ? trade.profit : '---'}
'; tradeLogDiv.innerHTML = tableHtml; } else { tradeLogDiv.innerHTML = '

No trades were executed.

'; } // Draw Equity Curve atbp_drawEquityChart(atbp_backtestResults.equityCurve); dlButton.style.display = 'block'; resultsTabLink.disabled = false; resultsTabLink.click(); } function atbp_drawEquityChart(equityData) { const svgContainer = d3.select("#atbp_equity_chart"); svgContainer.selectAll("*").remove(); const containerElement = svgContainer.node(); if (!containerElement) return; const clientRect = containerElement.getBoundingClientRect(); const containerWidth = clientRect.width; const containerHeight = clientRect.height > 100 ? clientRect.height : 350; // Ensure min height if (containerWidth <= 0 || equityData.length < 2) { svgContainer.append("text") .attr("x", containerWidth / 2) .attr("y", containerHeight / 2) .attr("text-anchor", "middle") .style("font-size", "14px") .style("fill", "#6b7280") .text(equityData.length < 2 ? "Not enough data for chart." : "Chart container not ready."); return; } const margin = {top: 20, right: 30, bottom: 40, left: 60}; const width = containerWidth - margin.left - margin.right; const height = containerHeight - margin.top - margin.bottom; const svg = svgContainer .attr("width", containerWidth) .attr("height", containerHeight) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`); const x = d3.scaleTime() .domain(d3.extent(equityData, d => d.date)) .range([0, width]); const y = d3.scaleLinear() .domain([0, d3.max(equityData, d => d.value) * 1.05]) // Add 5% padding to y-axis .range([height, 0]); // Add X axis svg.append("g") .attr("class", "axis x-axis") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(x).ticks(5).tickFormat(d3.timeFormat("%Y-%m-%d"))); // Add Y axis svg.append("g") .attr("class", "axis y-axis") .call(d3.axisLeft(y).ticks(5).tickFormat(d => atbp_formatCurrency(d).replace('$', ''))); // Remove $ for cleaner axis // Add Y gridlines svg.append("g") .attr("class", "grid") .call(d3.axisLeft(y) .ticks(5) .tickSize(-width) .tickFormat("") ); // Add the line svg.append("path") .datum(equityData) .attr("class", "line") .attr("d", d3.line() .x(d => x(d.date)) .y(d => y(d.value)) ); } window.atbp_downloadPDF = function() { const { jsPDF } = window.jspdf; if (!jsPDF || !atbp_backtestResults.inputs || Object.keys(atbp_backtestResults.inputs).length === 0) { alert("PDF library not loaded or no data to export. Please run a backtest first."); return; } const doc = new jsPDF(); let yPos = 20; const lineHeight = 7; const indent = 5; const pageMargin = 15; const pageWidth = doc.internal.pageSize.getWidth(); const usableWidth = pageWidth - 2 * pageMargin; doc.setFontSize(18); doc.setTextColor(28, 42, 56); // #1e2a38 doc.text("Algorithmic Trading Backtest Report", pageWidth / 2, yPos, { align: 'center' }); yPos += lineHeight * 2.5; function addSection(title, dataObj) { if (yPos > doc.internal.pageSize.getHeight() - pageMargin - 30) { doc.addPage(); yPos = pageMargin; } doc.setFontSize(13); doc.setFont(undefined, 'bold'); doc.setTextColor(44, 62, 80); // #2c3e50 doc.text(title, pageMargin, yPos); yPos += lineHeight * 1.5; doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.setTextColor(51,51,51); for (const [key, value] of Object.entries(dataObj)) { if (yPos > doc.internal.pageSize.getHeight() - pageMargin - lineHeight) { doc.addPage(); yPos = pageMargin; } let formattedKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase()); let valStr = String(value); if (typeof value === 'number') { if (key.toLowerCase().includes('percentage') || key.toLowerCase().includes('ratio') || key.toLowerCase().includes('period')) { valStr = value.toFixed(2) + (key.toLowerCase().includes('percentage') ? '%' : ''); } else if (key.toLowerCase().includes('capital') || key.toLowerCase().includes('value') || key.toLowerCase().includes('pl')) { valStr = atbp_formatCurrency(value); } else { valStr = value.toLocaleString(); } } doc.text(`${formattedKey}:`, pageMargin + indent, yPos); doc.text(valStr, pageMargin + indent + 75, yPos, {align: 'left', maxWidth: usableWidth - (indent + 75)}); yPos += lineHeight * 1.1; } yPos += lineHeight * 0.5; } addSection("Backtest Setup", { "Strategy Name": atbp_backtestResults.inputs.strategyName, "Strategy Type": atbp_backtestResults.inputs.strategyType.replace('_', ' '), "Asset Symbol": atbp_backtestResults.inputs.assetSymbol, "Short MA Period": atbp_backtestResults.inputs.shortMAPeriod, "Long MA Period": atbp_backtestResults.inputs.longMAPeriod, "Start Date": atbp_backtestResults.inputs.startDate, "End Date": atbp_backtestResults.inputs.endDate, "Initial Capital": atbp_backtestResults.inputs.initialCapital }); addSection("Performance Summary", { "Final Portfolio Value": atbp_backtestResults.performance.finalPortfolioValue, "Total P/L": atbp_backtestResults.performance.totalPL, "ROI Percentage": atbp_backtestResults.performance.roiPercentage, "Number of Trades": atbp_backtestResults.performance.numTrades, "Winning Trades": atbp_backtestResults.performance.winningTrades, "Losing Trades": atbp_backtestResults.performance.losingTrades, "Win/Loss Ratio": isFinite(atbp_backtestResults.performance.winLossRatio) ? atbp_backtestResults.performance.winLossRatio : 'N/A', "Max Drawdown Percentage": atbp_backtestResults.performance.maxDrawdownPercentage }); if (atbp_backtestResults.tradeLog && atbp_backtestResults.tradeLog.length > 0) { if (yPos > doc.internal.pageSize.getHeight() - pageMargin - 30) { doc.addPage(); yPos = pageMargin; } doc.setFontSize(13); doc.setFont(undefined, 'bold'); doc.setTextColor(44, 62, 80); doc.text("Trade Log (First 10 Trades)", pageMargin, yPos); yPos += lineHeight * 1.5; doc.setFontSize(8); doc.setFont(undefined, 'normal'); doc.setTextColor(51,51,51); const headers = ["Date", "Type", "Price", "Shares", "Value", "P/L"]; const colWidths = [25, 20, 25, 20, 30, 25]; let currentX = pageMargin + indent; headers.forEach((header, index) => { doc.text(header, currentX, yPos); currentX += colWidths[index]; }); yPos += lineHeight * 0.8; doc.setLineWidth(0.1); doc.line(pageMargin + indent, yPos, pageWidth - pageMargin - indent, yPos); yPos += lineHeight * 0.8; const tradesToShow = Math.min(atbp_backtestResults.tradeLog.length, 10); for(let i = 0; i < tradesToShow; i++) { const trade = atbp_backtestResults.tradeLog[i]; if (yPos > doc.internal.pageSize.getHeight() - pageMargin - lineHeight) { doc.addPage(); yPos = pageMargin; } currentX = pageMargin + indent; doc.text(trade.date, currentX, yPos); currentX += colWidths[0]; doc.text(trade.type, currentX, yPos); currentX += colWidths[1]; doc.text(trade.price, currentX, yPos); currentX += colWidths[2]; doc.text(String(trade.shares), currentX, yPos); currentX += colWidths[3]; doc.text(trade.value, currentX, yPos); currentX += colWidths[4]; doc.text(trade.profit !== undefined ? trade.profit : '---', currentX, yPos); yPos += lineHeight; } if (atbp_backtestResults.tradeLog.length > 10) { if (yPos > doc.internal.pageSize.getHeight() - pageMargin - lineHeight) { doc.addPage(); yPos = pageMargin; } doc.text("... and more trades.", pageMargin + indent, yPos); yPos += lineHeight; } } doc.save('Trading_Backtest_Report.pdf'); }
Scroll to Top