Stock Option Pricing Model (Black-Scholes)

Stock Option Pricing Model

${formatNum(pricingResults.callDelta)}

Vega

${formatNum(pricingResults.vega)}

Calculated Option Greeks

GreekCall ValuePut Value
Delta (Δ) ${formatNum(pricingResults.callDelta)} ${formatNum(pricingResults.putDelta)}
Gamma (Γ) ${formatNum(pricingResults.gamma)} ${formatNum(pricingResults.gamma)}
Theta (Θ) ${formatNum(pricingResults.callTheta)} ${formatNum(pricingResults.putTheta)}
Vega (ν) ${formatNum(pricingResults.vega)} ${formatNum(pricingResults.vega)}
Rho (ρ) ${formatNum(pricingResults.callRho)} ${formatNum(pricingResults.putRho)}
`; }; const renderInputsTab = () => { return `
`; }; // --- DATA & EVENT HANDLERS --- window.updateInput = (key, value) => { optionInputs[key] = (key === 'expirationDate') ? value : parseFloat(value) || 0; }; const handleCalculation = () => { pricingResults = blackScholes( optionInputs.stockPrice, optionInputs.strikePrice, optionInputs.riskFreeRate / 100, optionInputs.volatility / 100, getTimeToExpiration() ); switchTab(0); }; const getTimeToExpiration = () => { const today = new Date(); const expDate = new Date(optionInputs.expirationDate); // Set both to UTC midnight to avoid timezone issues affecting day count today.setUTCHours(0, 0, 0, 0); expDate.setUTCHours(0, 0, 0, 0); const diffTime = expDate.getTime() - today.getTime(); const diffDays = Math.max(0, diffTime / (1000 * 60 * 60 * 24)); return diffDays / 365.0; }; // --- BLACK-SCHOLES CALCULATION LOGIC --- function CND(x) { // Cumulative Normal Distribution let a1 = 0.319381530, a2 = -0.356563782, a3 = 1.781477937, a4 = -1.821255978, a5 = 1.330274429; let L = Math.abs(x); let k = 1.0 / (1.0 + 0.2316419 * L); let w = 1.0 - 1.0 / Math.sqrt(2 * Math.PI) * Math.exp(-L * L / 2) * (a1 * k + a2 * k * k + a3 * Math.pow(k, 3) + a4 * Math.pow(k, 4) + a5 * Math.pow(k, 5)); return x < 0 ? 1.0 - w : w; } function PDF(x) { // Probability Density Function return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI); } function blackScholes(S, K, r, sigma, T) { if (T <= 0) T = 0.000001; // Avoid division by zero for expired options let d1 = (Math.log(S / K) + (r + sigma * sigma / 2.0) * T) / (sigma * Math.sqrt(T)); let d2 = d1 - sigma * Math.sqrt(T); let callPrice = S * CND(d1) - K * Math.exp(-r * T) * CND(d2); let putPrice = K * Math.exp(-r * T) * CND(-d2) - S * CND(-d1); // Greeks let callDelta = CND(d1); let putDelta = callDelta - 1; let gamma = PDF(d1) / (S * sigma * Math.sqrt(T)); let vega = S * PDF(d1) * Math.sqrt(T) / 100; // per 1% change let callTheta = (- (S * PDF(d1) * sigma) / (2 * Math.sqrt(T)) - r * K * Math.exp(-r * T) * CND(d2)) / 365; // per day let putTheta = (- (S * PDF(d1) * sigma) / (2 * Math.sqrt(T)) + r * K * Math.exp(-r * T) * CND(-d2)) / 365; // per day let callRho = (K * T * Math.exp(-r * T) * CND(d2)) / 100; // per 1% change let putRho = (-K * T * Math.exp(-r * T) * CND(-d2)) / 100; // per 1% change return { callPrice, putPrice, callDelta, putDelta, gamma, vega, callTheta, putTheta, callRho, putRho }; } // --- CHART & PDF --- const renderChart = () => { if (payoffChart) payoffChart.destroy(); const ctx = document.getElementById('payoffChart')?.getContext('2d'); if (!ctx || !pricingResults) return; const K = optionInputs.strikePrice; const callP = pricingResults.callPrice; const putP = pricingResults.putPrice; const labels = []; const callPayoff = []; const putPayoff = []; for (let i = -0.4; i <= 0.4; i += 0.05) { let stockPriceAtExp = K * (1 + i); labels.push(stockPriceAtExp.toFixed(2)); callPayoff.push(Math.max(0, stockPriceAtExp - K) - callP); putPayoff.push(Math.max(0, K - stockPriceAtExp) - putP); } payoffChart = new Chart(ctx, { type: 'line', data: { labels, datasets: [ { label: 'Call Option P/L', data: callPayoff, borderColor: '#16a34a', backgroundColor: 'rgba(22, 163, 74, 0.1)', fill: true, tension: 0.1 }, { label: 'Put Option P/L', data: putPayoff, borderColor: '#dc2626', backgroundColor: 'rgba(220, 38, 38, 0.1)', fill: true, tension: 0.1 } ] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: 'Profit/Loss at Expiration' } }, scales: { x: { title: { display: true, text: 'Stock Price at Expiration ($)' } }, y: { title: { display: true, text: 'Profit / Loss ($)' } } } } }); }; window.downloadPDF = () => { if (!pricingResults) return; const { jsPDF } = window.jspdf; const doc = new jsPDF(); doc.setFontSize(20); doc.text("Stock Option Pricing Report", doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' }); doc.setFontSize(14); doc.text("Input Parameters", 14, 35); doc.autoTable({ startY: 40, body: [ ['Stock Price', `$${optionInputs.stockPrice.toFixed(2)}`], ['Strike Price', `$${optionInputs.strikePrice.toFixed(2)}`], ['Expiration Date', optionInputs.expirationDate], ['Volatility', `${optionInputs.volatility.toFixed(2)}%`], ['Risk-Free Rate', `${optionInputs.riskFreeRate.toFixed(2)}%`], ], theme: 'grid', styles: { cellPadding: 2 }, headStyles: { fillColor: false, textColor: 0 }, columnStyles: { 0: { fontStyle: 'bold' } }, }); const finalY1 = doc.lastAutoTable.finalY + 15; doc.setFontSize(14); doc.text("Pricing & Greeks", 14, finalY1); const head = [['Metric', 'Call Value', 'Put Value']]; const body = [ ['Price', `$${pricingResults.callPrice.toFixed(2)}`, `$${pricingResults.putPrice.toFixed(2)}`], ['Delta (Δ)', pricingResults.callDelta.toFixed(4), pricingResults.putDelta.toFixed(4)], ['Gamma (Γ)', pricingResults.gamma.toFixed(4), pricingResults.gamma.toFixed(4)], ['Theta (Θ)', pricingResults.callTheta.toFixed(4), pricingResults.putTheta.toFixed(4)], ['Vega (ν)', pricingResults.vega.toFixed(4), pricingResults.vega.toFixed(4)], ['Rho (ρ)', pricingResults.callRho.toFixed(4), pricingResults.putRho.toFixed(4)], ]; doc.autoTable({ startY: finalY1 + 5, head: head, body: body, theme: 'striped', headStyles: { fillColor: [55, 48, 163] } }); doc.save('Option-Pricing-Report.pdf'); }; // --- INITIALIZATION --- DOM.nextBtn.addEventListener('click', () => switchTab(currentTab + 1)); DOM.prevBtn.addEventListener('click', () => switchTab(currentTab - 1)); DOM.tabButtons.forEach((btn, i) => btn.addEventListener('click', () => switchTab(i))); renderCurrentTab(); updateNavButtons(); });
Scroll to Top