`;
document.body.appendChild(overlay);
overlay.querySelector('.modal-ok-btn').onclick = () => overlay.remove();
}
/**
* Custom confirmation box function.
* @param {string} message - The message to display.
* @param {Function} onConfirm - Callback function if confirmed.
*/
function showCustomConfirmBox(message, onConfirm) {
const overlay = document.createElement('div');
overlay.classList.add('custom-modal-overlay');
overlay.innerHTML = `
`;
document.body.appendChild(overlay);
overlay.querySelector('.modal-yes-btn').onclick = () => {
onConfirm();
overlay.remove();
};
overlay.querySelector('.modal-no-btn').onclick = () => {
overlay.remove();
};
}
// --- Tab Navigation Logic ---
/**
* Shows the specified tab and hides others. Updates active tab buttons.
* @param {number} tabIndex - The index of the tab to show.
*/
function showTab(tabIndex) {
if (tabIndex < 0 || tabIndex >= tabContents.length) {
console.error("Invalid tab index:", tabIndex);
return;
}
tabContents.forEach(content => content.classList.remove('active'));
tabButtons.forEach(button => button.classList.remove('active'));
tabContents[tabIndex].classList.add('active');
tabButtons[tabIndex].classList.add('active');
currentTab = tabIndex;
prevTabBtn.disabled = (currentTab === 0);
nextTabBtn.disabled = (currentTab === tabContents.length - 1);
// Re-render results if on the assessment tab
if (currentTab === 2) {
renderAssessmentResults();
}
}
/**
* Navigates to the next or previous tab.
* @param {number} direction - -1 for previous, 1 for next.
*/
window.navigateTab = function(direction) {
const newTab = currentTab + direction;
showTab(newTab);
}
// Add event listeners for tab buttons
tabButtons.forEach((button, index) => {
button.addEventListener('click', function() {
showTab(index);
});
});
// --- Investment Details Processing ---
/**
* Captures investment details and moves to the next tab.
*/
window.processInvestmentDetails = function() {
const investmentName = document.getElementById('investmentName').value.trim();
const assetClass = document.getElementById('assetClass').value;
const investmentAmount = parseFloat(document.getElementById('investmentAmount').value);
const investmentDateStr = document.getElementById('investmentDate').value;
const expectedHoldingPeriod = parseInt(document.getElementById('expectedHoldingPeriod').value);
const targetIRR = parseFloat(document.getElementById('targetIRR').value);
const notes = document.getElementById('notes').value.trim();
// Basic validation
if (!investmentName) { showCustomMessageBox('Please enter an Investment Name.'); return; }
if (isNaN(investmentAmount) || investmentAmount <= 0) { showCustomMessageBox('Please enter a valid Investment Amount.'); return; }
if (!investmentDateStr) { showCustomMessageBox('Please enter an Investment Date.'); return; }
if (isNaN(expectedHoldingPeriod) || expectedHoldingPeriod <= 0) { showCustomMessageBox('Please enter a valid Expected Holding Period in years.'); return; }
if (isNaN(targetIRR) || targetIRR < 0) { showCustomMessageBox('Please enter a valid Target IRR (0% or more).'); return; }
liquidityAssessmentData.investment = {
name: investmentName,
assetClass: assetClass,
amount: investmentAmount,
date: new Date(investmentDateStr + 'T00:00:00'),
holdingPeriod: expectedHoldingPeriod,
targetIRR: targetIRR,
notes: notes
};
showTab(1); // Move to Liquidity Factors tab
}
// --- Liquidity Factors Assessment ---
/**
* Captures liquidity factors and moves to the assessment results tab.
*/
window.assessLiquidityFactors = function() {
const marketConditions = document.getElementById('marketConditions').value;
const secondaryMarketActivity = document.getElementById('secondaryMarketActivity').value;
const investorDemand = document.getElementById('investorDemand').value;
const exitStrategy = document.getElementById('exitStrategy').value;
const dealComplexity = document.getElementById('dealComplexity').value;
const regulatoryEnvironment = document.getElementById('regulatoryEnvironment').value;
liquidityAssessmentData.factors = {
marketConditions: marketConditions,
secondaryMarketActivity: secondaryMarketActivity,
investorDemand: investorDemand,
exitStrategy: exitStrategy,
dealComplexity: dealComplexity,
regulatoryEnvironment: regulatoryEnvironment
};
showTab(2); // Move to Assessment Results tab
}
// --- Assessment Results Calculation & Display ---
/**
* Calculates and renders the liquidity assessment results.
* This is a simplified illustrative model.
*/
function renderAssessmentResults() {
if (!liquidityAssessmentData.investment || !liquidityAssessmentData.factors) {
liquidityResultsDisplay.innerHTML = '
`;
pdfContentContainer.innerHTML = pdfHtml;
// Temporarily make the container visible (but off-screen) for html2canvas
pdfContentContainer.style.display = 'block';
const canvas = await html2canvas(pdfContentContainer, {
scale: 2,
logging: false,
useCORS: true,
// Fix: Ensure background is rendered correctly
backgroundColor: '#ffffff'
});
// Hide the container again immediately
pdfContentContainer.style.display = 'none';
const imgData = canvas.toDataURL('image/png');
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'mm', 'a4');
const imgWidth = 210;
const pageHeight = 297;
const imgHeight = canvas.height * imgWidth / canvas.width;
let heightLeft = imgHeight;
let position = 0;
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft > 0) {
position = -heightLeft; // Adjust position for new page
doc.addPage();
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save('Private_Market_Liquidity_Report.pdf');
pdfContentContainer.innerHTML = ''; // Clean up after download
}
// --- Initial Setup ---
showTab(0); // Show the first tab on load
});
Please complete "Investment Details" and "Liquidity Factors" tabs first.
'; return; } const inv = liquidityAssessmentData.investment; const fac = liquidityAssessmentData.factors; let estimatedTimeToLiquidity = inv.holdingPeriod; // Base time in years let potentialLiquidityDiscount = 0; // Base discount % let liquidityRiskScore = 'Medium'; // Default // Adjust Time to Liquidity based on factors if (fac.marketConditions === 'Bear') estimatedTimeToLiquidity += 2; if (fac.marketConditions === 'Bull') estimatedTimeToLiquidity = Math.max(1, estimatedTimeToLiquidity - 1); if (fac.secondaryMarketActivity === 'Low' || fac.secondaryMarketActivity === 'Non-existent') estimatedTimeToLiquidity += 3; if (fac.secondaryMarketActivity === 'High') estimatedTimeToLiquidity = Math.max(1, estimatedTimeToLiquidity - 0.5); if (fac.exitStrategy === 'Liquidation') estimatedTimeToLiquidity -= 1; // Faster but at a cost if (fac.exitStrategy === 'IPO') estimatedTimeToLiquidity += 2; // Longer process // Adjust Liquidity Discount based on factors switch (inv.assetClass) { case 'Private Equity': potentialLiquidityDiscount += 15; break; case 'Private Credit': potentialLiquidityDiscount += 10; break; case 'Real Estate': potentialLiquidityDiscount += 12; break; case 'Infrastructure': potentialLiquidityDiscount += 8; break; case 'Venture Capital': potentialLiquidityDiscount += 25; break; case 'Other': potentialLiquidityDiscount += 18; break; } if (fac.marketConditions === 'Bear') potentialLiquidityDiscount += 10; if (fac.marketConditions === 'Volatile') potentialLiquidityDiscount += 5; if (fac.marketConditions === 'Bull') potentialLiquidityDiscount = Math.max(0, potentialLiquidityDiscount - 5); if (fac.secondaryMarketActivity === 'Low' || fac.secondaryMarketActivity === 'Non-existent') potentialLiquidityDiscount += 15; if (fac.investorDemand === 'Low') potentialLiquidityDiscount += 7; if (fac.dealComplexity === 'High') potentialLiquidityDiscount += 5; if (fac.regulatoryEnvironment === 'Restrictive') potentialLiquidityDiscount += 3; // Determine Liquidity Risk Score if (estimatedTimeToLiquidity > 7 || potentialLiquidityDiscount > 20) { liquidityRiskScore = 'High'; } else if (estimatedTimeToLiquidity > 4 || potentialLiquidityDiscount > 10) { liquidityRiskScore = 'Medium'; } else { liquidityRiskScore = 'Low'; } // Store calculated results liquidityAssessmentData.results = { estimatedTimeToLiquidity: estimatedTimeToLiquidity.toFixed(1), // Years potentialLiquidityDiscount: potentialLiquidityDiscount.toFixed(2), // Percentage liquidityRiskScore: liquidityRiskScore }; let html = `Based on your inputs, here is the preliminary liquidity assessment:
Investment Name:
${inv.name}
Asset Class:
${inv.assetClass}
Investment Amount:
${formatCurrency(inv.amount)}
Estimated Time to Liquidity:
${liquidityAssessmentData.results.estimatedTimeToLiquidity} Years
Potential Liquidity Discount:
${formatPercentage(liquidityAssessmentData.results.potentialLiquidityDiscount)}
Liquidity Risk Score:
${liquidityAssessmentData.results.liquidityRiskScore}
${inv.notes ? `
Notes:
${inv.notes}
` : ''}
`;
liquidityResultsDisplay.innerHTML = html;
}
// --- PDF Generation Logic ---
/**
* Generates a PDF report of the liquidity assessment.
*/
window.generatePDF = async function() {
if (!liquidityAssessmentData.investment || !liquidityAssessmentData.factors || !liquidityAssessmentData.results) {
showCustomMessageBox('Please complete all sections of the tool before generating the PDF report.');
return;
}
pdfContentContainer.innerHTML = ''; // Clear previous content
const inv = liquidityAssessmentData.investment;
const fac = liquidityAssessmentData.factors;
const res = liquidityAssessmentData.results;
let pdfHtml = `
Private Market Liquidity Report
Investment Details
Investment Name:${inv.name}
Asset Class:${inv.assetClass}
Investment Amount:${formatCurrency(inv.amount)}
Investment Date:${formatDate(inv.date)}
Expected Holding Period:${inv.holdingPeriod} Years
Target IRR:${formatPercentage(inv.targetIRR)}
${inv.notes ? `Notes:${inv.notes}
` : ''}
Liquidity Factors
Current Market Conditions:${fac.marketConditions}
Secondary Market Activity:${fac.secondaryMarketActivity}
General Investor Demand:${fac.investorDemand}
Primary Exit Strategy:${fac.exitStrategy}
Deal Complexity / Covenants:${fac.dealComplexity}
Regulatory Environment:${fac.regulatoryEnvironment}
Liquidity Assessment Results
Estimated Time to Liquidity:${res.estimatedTimeToLiquidity} Years
Potential Liquidity Discount:${formatPercentage(res.potentialLiquidityDiscount)}
Liquidity Risk Score:${res.liquidityRiskScore}