Enter Sale Transaction Details

Enter the details of the security sold at a loss.

Enter Purchase Transactions of Substantially Identical Securities

Add any purchases of the same or substantially identical security made 30 days before or 30 days after the sale date.

Wash Sale Analysis Results

The sale did not result in a loss. The Wash Sale Rule does not apply for disallowing losses on this transaction.

`; resultsOutputEl.innerHTML = html; return; } const windowStart = new Date(saleDate); windowStart.setDate(saleDate.getDate() - 30); const windowEnd = new Date(saleDate); windowEnd.setDate(saleDate.getDate() + 30); html += `
Wash Sale Window: ${windowStart.toLocaleDateString()} - ${windowEnd.toLocaleDateString()} (61 days)
`; const relevantPurchases = purchases .map(p => ({...p, purchaseDateObj: new Date(p.date + "T00:00:00")})) .filter(p => p.purchaseDateObj >= windowStart && p.purchaseDateObj <= windowEnd) .sort((a, b) => a.purchaseDateObj - b.purchaseDateObj); // Sort by purchase date let totalDisallowedLoss = 0; let remainingSharesSoldAtLoss = sale.quantity; let affectedPurchaseDetails = []; if (relevantPurchases.length === 0) { html += `

No Wash Sale Detected

No substantially identical securities were purchased within the 61-day wash sale window based on the provided purchase transactions. The loss of ${formatCurrency(totalLossOnSale)} may be fully deductible (subject to other tax rules).

`; } else { html += `

Potential Wash Sale Analysis

`; for (const purchase of relevantPurchases) { if (remainingSharesSoldAtLoss <= 0) break; const sharesTriggering = Math.min(remainingSharesSoldAtLoss, purchase.quantity); const disallowedLossForThisLot = sharesTriggering * lossOnSalePerShare; const adjustedBasisPerShare = purchase.price + (disallowedLossForThisLot / sharesTriggering); // (lossOnSalePerShare) totalDisallowedLoss += disallowedLossForThisLot; remainingSharesSoldAtLoss -= sharesTriggering; affectedPurchaseDetails.push({ date: purchase.purchaseDateObj.toLocaleDateString(), symbol: purchase.symbol, qtyPurchased: purchase.quantity, price: purchase.price, sharesTriggering: sharesTriggering, disallowedLossPortion: disallowedLossForThisLot, adjustedBasis: adjustedBasisPerShare }); html += ``; } html += `
Purchase DateSymbolQty PurchasedPriceShares Triggering Wash SaleDisallowed Loss PortionAdjusted Basis/Share
${purchase.purchaseDateObj.toLocaleDateString()} ${purchase.symbol} ${purchase.quantity.toLocaleString()} ${formatCurrency(purchase.price)} ${sharesTriggering.toLocaleString()} ${formatCurrency(disallowedLossForThisLot)} ${formatCurrency(adjustedBasisPerShare)}
`; html += `
Total Disallowed Loss: ${formatCurrency(totalDisallowedLoss)}
`; const allowableLoss = totalLossOnSale - totalDisallowedLoss; html += `
Net Allowable Loss (for this sale): ${formatCurrency(allowableLoss)}
`; html += `

The disallowed loss is added to the cost basis of the replacement shares. The holding period of the replacement shares (for the quantity triggering the wash sale) includes the holding period of the shares sold.

`; } saleDetailsStorage.analysisHTML = html; // Store for PDF saleDetailsStorage.affectedPurchases = affectedPurchaseDetails; // Store for PDF saleDetailsStorage.totalDisallowedLoss = totalDisallowedLoss; saleDetailsStorage.totalLossOnSale = totalLossOnSale; saleDetailsStorage.windowStart = windowStart.toLocaleDateString(); saleDetailsStorage.windowEnd = windowEnd.toLocaleDateString(); resultsOutputEl.innerHTML = html; } // --- Helper: Format Currency --- function formatCurrency(amount, defaultOnError = '$0.00') { if (typeof amount !== 'number' || isNaN(amount)) return defaultOnError; return `\$${amount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; } // --- Reset All --- if (resetAllButton) { resetAllButton.addEventListener('click', function() { saleSymbolEl.value = ''; saleDateEl.value = ''; saleQuantityEl.value = ''; salePriceEl.value = ''; saleCostBasisEl.value = ''; document.querySelectorAll('.wsc-error-message').forEach(el => el.textContent = ''); purchaseEntriesContainer.innerHTML = ''; purchaseEntryIdCounter = 0; resultsOutputEl.innerHTML = ''; saleDetailsStorage = {}; purchasesTabButton.disabled = true; analysisTabButton.disabled = true; switchTab('wscSaleTab', true); }); } // --- PDF Download --- if(downloadPdfButton) { downloadPdfButton.addEventListener('click', function() { if (!saleDetailsStorage || !saleDetailsStorage.analysisHTML) { alert("Please perform an analysis first."); return; } const { symbol, date, quantity, price, costBasis, totalLossOnSale, windowStart, windowEnd, totalDisallowedLoss, affectedPurchases } = saleDetailsStorage; let purchasesTableHTML = ''; if (affectedPurchases && affectedPurchases.length > 0) { purchasesTableHTML = `

Wash Sale Details & Basis Adjustments

`; affectedPurchases.forEach(p => { purchasesTableHTML += ``; }); purchasesTableHTML += `
Purchase DateSymbolQty PurchasedPriceShares TriggeringDisallowed LossAdjusted Basis/Share
${p.date}${p.symbol}${p.qtyPurchased.toLocaleString()} ${formatCurrency(p.price)}${p.sharesTriggering.toLocaleString()} ${formatCurrency(p.disallowedLossPortion)}${formatCurrency(p.adjustedBasis)}
`; } else if (totalLossOnSale > 0) { // Loss occurred, but no relevant purchases purchasesTableHTML = `

No substantially identical securities were purchased within the 61-day wash sale window. Loss may be fully deductible.

` } const printStyles = ` body { font-family: var(--wsc-font-family, Arial); color: #333; margin: 20px; padding: 10px; font-size: 10pt; } h2 { color: var(--wsc-primary-color, #2E8B57); text-align: center; border-bottom: 1px solid #ccc; padding-bottom: 5px; margin-bottom:15px; font-size: 14pt; } h3, h4 { color: var(--wsc-dark-color, #2F4F4F); margin-top: 15px; margin-bottom:8px; font-size: 11pt; border-bottom: 1px solid #eee; padding-bottom: 3px;} .wsc-info-item-pdf { display: flex; justify-content: space-between; padding: 4px 0; border-bottom: 1px dotted #eee; margin-bottom: 3px; } .wsc-info-item-pdf strong { flex-basis: 50%; } .wsc-info-item-pdf span { text-align: right; flex-basis: 50%; font-weight: bold; } .wsc-results-table-pdf { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 9pt; } .wsc-results-table-pdf th, .wsc-results-table-pdf td { border: 1px solid #ccc; padding: 5px; text-align: left; } .wsc-results-table-pdf th { background-color: #f0f0f0; } .wsc-loss { color: #D9534F; } .wsc-adjusted { color: #5BC0DE; } p { margin-bottom: 10px; line-height: 1.4; } .footer-note {font-size:0.8em; margin-top:20px; text-align:center; color: #777;} `; const reportBodyHTML = `

Wash Sale Rule Compliance Check

Sale Transaction Summary

Security Sold: ${symbol}
Sale Date: ${new Date(date + "T00:00:00").toLocaleDateString()}
Quantity Sold: ${quantity.toLocaleString()}
Sale Price/Share: ${formatCurrency(price)}
Cost Basis/Share: ${formatCurrency(costBasis)}
Total Loss on Sale: ${formatCurrency(totalLossOnSale)}
${totalLossOnSale > 0 ? `
Wash Sale Window: ${windowStart} - ${windowEnd}
${purchasesTableHTML}
Total Disallowed Loss: ${formatCurrency(totalDisallowedLoss)}
Net Allowable Loss: ${formatCurrency(totalLossOnSale - totalDisallowedLoss)}

The disallowed loss is added to the cost basis of the replacement shares. The holding period of such replacement shares includes the holding period of the shares sold.

` : '

The sale did not result in a loss. Wash Sale Rule does not apply for disallowing losses.

'} `; const printWindow = window.open('', '_blank'); if (printWindow) { printWindow.document.open(); printWindow.document.write(` Wash Sale Analysis Report ${reportBodyHTML}`); printWindow.document.close(); setTimeout(() => { printWindow.focus(); printWindow.print(); /* printWindow.close(); */ }, 500); } else { alert('Could not open print window. Please check pop-up blocker settings.'); } }); } // Initialize first tab switchTab('wscSaleTab'); addPurchaseEntry(); // Add one purchase entry form by default });
Scroll to Top