Covered Call Writing Strategy Tester

Covered Call Writing Strategy Tester

Strategy Parameters

This commission is applied for stock purchase, call sale, and potential stock sale/assignment.

$${results.breakevenPrice.toFixed(2)}

Maximum Profit:

$${results.maxProfit.toFixed(2)}

Maximum Loss (Stock at $0):

$${results.maxLoss.toFixed(2)}

Return on Capital (if assigned):

${results.returnOnCapital.toFixed(2)}%

`; let detailedTableHtml = `

Profit/Loss Scenarios at Expiration

Scenario Underlying Price ($) P&L ($)
Current Stock Price ${params.stockPurchasePrice.toFixed(2)} $${calculateCoveredCallPnL(params, params.stockPurchasePrice).toFixed(2)}
Breakeven Price ${results.breakevenPrice.toFixed(2)} $0.00
Call Strike Price (Assigned) ${params.callStrikePrice.toFixed(2)} $${results.maxProfit.toFixed(2)}
Underlying Price at $0.00 $0.00 $${results.maxLoss.toFixed(2)}
`; resultsOutput.innerHTML = summaryHtml + detailedTableHtml; } /** * Renders the Profit & Loss chart using Chart.js. * @param {Object} results - The results object containing chart data. */ function renderChart(results) { if (!results.chart || !results.chart.labels || results.chart.labels.length === 0) { chartPlaceholder.classList.remove('hidden'); if (plChartInstance) { plChartInstance.destroy(); plChartInstance = null; } return; } chartPlaceholder.classList.add('hidden'); // Hide placeholder if chart can be rendered const ctx = document.getElementById('plChart').getContext('2d'); // Destroy existing chart instance if it exists to prevent overlap if (plChartInstance) { plChartInstance.destroy(); } plChartInstance = new Chart(ctx, { type: 'line', // Line chart for P&L over price range data: { labels: results.chart.labels, datasets: [{ label: `P&L for ${results.assetSymbol} Covered Call`, data: results.chart.data, borderColor: '#3B82F6', // Blue 500 backgroundColor: 'rgba(59, 130, 246, 0.2)', // Light blue fill borderWidth: 2, tension: 0.1, // Smooth curve pointRadius: 0, // No points for a clean line fill: true, // Fill area under the curve }, { // Dataset for Breakeven line label: 'Breakeven', data: results.chart.labels.map(price => parseFloat(price) === parseFloat(results.breakevenPrice.toFixed(2)) ? 0 : null), borderColor: '#F59E0B', // Amber 500 borderWidth: 2, borderDash: [5, 5], // Dashed line pointRadius: 5, pointBackgroundColor: '#F59E0B', showLine: true, fill: false, }, { // Dataset for Max Profit line (at strike price) label: 'Max Profit (at Strike)', data: results.chart.labels.map(price => parseFloat(price) === parseFloat(results.strategyParams.callStrikePrice.toFixed(2)) ? results.maxProfit.toFixed(2) : null), borderColor: '#10B981', // Emerald 500 borderWidth: 2, borderDash: [5, 5], pointRadius: 5, pointBackgroundColor: '#10B981', showLine: true, fill: false, } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'top', labels: { font: { size: 14, family: 'Inter', }, color: '#333' } }, title: { display: true, text: `Covered Call P&L for ${results.assetSymbol}`, font: { size: 18, family: 'Inter', weight: 'bold' }, color: '#1F2937' }, tooltip: { mode: 'index', intersect: false, callbacks: { label: function(context) { let label = context.dataset.label || ''; if (label) { label += ': '; } if (context.parsed.y !== null) { label += `$${parseFloat(context.parsed.y).toFixed(2)}`; } return label; }, title: function(context) { return `Price: $${context[0].label}`; } } } }, scales: { y: { beginAtZero: false, title: { display: true, text: 'Profit / Loss ($)', font: { size: 14, family: 'Inter' }, color: '#333' }, ticks: { callback: function(value) { return '$' + value; }, font: { family: 'Inter' } } }, x: { title: { display: true, text: 'Underlying Asset Price at Expiration ($)', font: { size: 14, family: 'Inter' }, color: '#333' }, ticks: { font: { family: 'Inter' } } } } } }); } /** * Handles the PDF download functionality. * Captures the results output and chart canvas to generate a PDF report. */ window.downloadPdf = async function() { // Ensure jsPDF and html2canvas libraries are loaded if (typeof html2canvas === 'undefined' || typeof jspdf === 'undefined' || typeof jspdf.jsPDF === 'undefined') { displayMessage('PDF generation libraries not loaded. Please try again or refresh.'); return; } hideMessage(); // Clear any existing messages const { jsPDF } = jspdf; const doc = new jsPDF('p', 'pt', 'a4'); // 'p' for portrait, 'pt' for points, 'a4' for A4 size // Define the elements to be captured for the PDF report const elementsToCapture = [ document.getElementById('resultsOutput'), document.getElementById('plChart') ]; // Create promises for capturing each element as a canvas image const promises = elementsToCapture.map(element => { // Temporarily make the element visible if it's currently hidden const wasHidden = element.classList.contains('hidden'); if (wasHidden) { element.classList.remove('hidden'); } return html2canvas(element, { scale: 2, // Increase scale for better resolution in PDF useCORS: true, // Required for images/fonts loaded from other origins if any backgroundColor: '#ffffff' // Ensure white background for captured content }).then(canvas => { // Restore hidden state if it was temporarily made visible if (wasHidden) { element.classList.add('hidden'); } return canvas; }); }); let yPos = 40; // Initial Y position for content in the PDF // Add a main title to the PDF report doc.setFontSize(22); doc.setTextColor(51, 51, 51); // Dark gray doc.text('Covered Call Writing Strategy Simulation Report', doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 30; // Move down for next element // Add generation date doc.setFontSize(12); doc.setTextColor(100, 100, 100); // Medium gray doc.text(`Generated on: ${new Date().toLocaleDateString()}`, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 40; // Move down for next element // Iterate through the captured canvases and add them to the PDF for (const promise of promises) { try { const canvas = await promise; const imgData = canvas.toDataURL('image/png'); // Convert canvas to PNG data URL const imgWidth = 550; // Desired width for the image in the PDF (A4 is approx 595pt wide) const imgHeight = (canvas.height * imgWidth) / canvas.width; // Maintain aspect ratio // Check if the image fits on the current page. If not, add a new page. if (yPos + imgHeight > doc.internal.pageSize.getHeight() - 40) { // Check against a 40pt bottom margin doc.addPage(); // Add a new page yPos = 40; // Reset Y position for the new page } // Add the image to the PDF, centered horizontally doc.addImage(imgData, 'PNG', (doc.internal.pageSize.getWidth() - imgWidth) / 2, yPos, imgWidth, imgHeight); yPos += imgHeight + 30; // Move Y position down, adding some padding } catch (error) { console.error('Error capturing element for PDF:', error); displayMessage('Failed to generate part of the PDF. Please ensure all data is loaded.'); } } // Save the generated PDF file doc.save('Covered_Call_Strategy_Simulation.pdf'); }; // Initial setup: Show the input tab and update navigation buttons when the DOM is ready showTab('input'); });
Scroll to Top