Actuarial Risk Model Parameter File

Actuarial Risk Model Parameter Manager

Add, edit, or remove Lines of Business (LOBs) and their associated parameters. (Spec #6: Manual Input)

Calculates a simple gross premium based on parameters (if available).

Result will appear here.
`; // Set dashboard content (Spec II.A) dashboardContent.innerHTML = tableHtml + calculatorHtml; // Set PDF content (Spec II.C.2 - excludes interactive elements) pdfOutputContent.innerHTML = `

Actuarial Parameters

` + pdfTableHtml; } /** * Calculator function - attached to window for inline onclick */ window.actuarialCalculatePremium = function () { if (!lobSelect) return; const resultEl = document.getElementById("calc-result"); const basePremiumEl = document.getElementById("calc-base-premium"); const ldfEl = document.getElementById("calc-ldf"); if (!resultEl || !basePremiumEl || !ldfEl) return; const lobName = lobSelect.value; const params = actuarialData[lobName]; if (!params) { resultEl.textContent = "Error: LOB data not found."; return; } const basePremium = parseFloat(basePremiumEl.value) || 0; const ldf = parseFloat(ldfEl.value) || 1.0; const discountRate = parseFloat(params["Discount Rate"] || 0); const expenseRatio = parseFloat(params["Expense Ratio"] || 0); // Simple illustrative formula: (Base * LDF) / (1 - Disc) / (1 - Exp) if ( basePremium === 0 || discountRate >= 1 || expenseRatio >= 1 ) { resultEl.textContent = "Error: Invalid inputs."; return; } const ultimateLoss = basePremium * ldf; const discountedLoss = ultimateLoss / (1 + discountRate); // Simplified PV const grossPremium = discountedLoss / (1 - expenseRatio); // Spec I, II.D.3: Use $ resultEl.textContent = `Gross Premium: $${grossPremium.toFixed(2)}`; }; /** * Builds the configuration tab inputs from the actuarialData object */ function buildConfig() { if (!configContainer) return; // Spec IV.B.2 configContainer.innerHTML = ""; let lobIndex = 0; for (const lobName in actuarialData) { const lobId = `lob-${lobIndex}`; const lobDiv = document.createElement("div"); lobDiv.className = "actuarial-config-lob"; lobDiv.id = lobId; const header = document.createElement("div"); header.className = "actuarial-config-lob-header"; header.innerHTML = ` `; lobDiv.appendChild(header); const paramsContainer = document.createElement("div"); paramsContainer.className = "actuarial-config-params-container"; paramsContainer.id = `${lobId}-params`; let paramIndex = 0; for (const [key, value] of Object.entries(actuarialData[lobName])) { const paramId = `${lobId}-param-${paramIndex}`; const paramDiv = document.createElement("div"); paramDiv.className = "actuarial-config-param"; paramDiv.id = paramId; paramDiv.innerHTML = `
`; paramsContainer.appendChild(paramDiv); paramIndex++; } lobDiv.appendChild(paramsContainer); const addParamBtnDiv = document.createElement("div"); addParamBtnDiv.className = "actuarial-config-add-param-btn"; addParamBtnDiv.innerHTML = ``; lobDiv.appendChild(addParamBtnDiv); configContainer.appendChild(lobDiv); lobIndex++; } } /** * Saves the data from the config tab back to the actuarialData object * and reloads the dashboard */ window.saveActuarialConfig = function () { if (!configContainer || !saveMessage) return; const newActuarialData = {}; const lobDivs = configContainer.querySelectorAll(".actuarial-config-lob"); lobDivs.forEach((lobDiv) => { const lobNameInput = lobDiv.querySelector( ".actuarial-config-lob-name" ); if (!lobNameInput) return; const lobName = lobNameInput.value.trim(); if (!lobName) return; newActuarialData[lobName] = {}; const paramDivs = lobDiv.querySelectorAll(".actuarial-config-param"); paramDivs.forEach((paramDiv) => { const keyInput = paramDiv.querySelector( ".actuarial-config-param-key" ); const valueInput = paramDiv.querySelector( ".actuarial-config-param-value" ); if (!keyInput || !valueInput) return; const key = keyInput.value.trim(); const value = parseFloat(valueInput.value); if (key && !isNaN(value)) { newActuarialData[lobName][key] = value; } }); }); actuarialData = newActuarialData; // Update the global data object buildDashboard(); // Re-build dashboard (Tab 1) // Show save message (Spec compliant: No alert()) saveMessage.style.display = "inline"; setTimeout(() => { saveMessage.style.display = "none"; }, 2000); showActuarialTab("actuarial-tab-dashboard"); // Switch back to dashboard }; /** * Adds a new empty LOB to the config tab */ window.addActuarialConfigLOB = function () { if (!configContainer) return; const newLobId = `lob-new-${Date.now()}`; const lobDiv = document.createElement("div"); lobDiv.className = "actuarial-config-lob"; lobDiv.id = newLobId; lobDiv.innerHTML = `
`; configContainer.appendChild(lobDiv); }; /** * Adds a new empty parameter to a LOB in the config tab */ window.addActuarialConfigParam = function (paramsContainerId) { const paramsContainer = document.getElementById(paramsContainerId); if (!paramsContainer) return; const newParamId = `${paramsContainerId}-param-new-${Date.now()}`; const paramDiv = document.createElement("div"); paramDiv.className = "actuarial-config-param"; paramDiv.id = newParamId; paramDiv.innerHTML = `
`; paramsContainer.appendChild(paramDiv); }; /** * Removes a DOM element (LOB or param) from the config tab */ window.removeActuarialElement = function (elementId) { const element = document.getElementById(elementId); if (element && element.parentNode) { element.parentNode.removeChild(element); } }; /** * Switches the active tab * (Spec II.B.4) */ window.showActuarialTab = function (tabId) { if (!tabButtons.length || !tabContents.length) return; tabContents.forEach((content) => { content.classList.remove("active"); }); tabButtons.forEach((button) => { button.classList.remove("active"); }); const contentToShow = document.getElementById(tabId); const buttonToActivate = document.querySelector( `[onclick="showActuarialTab('${tabId}')"]` ); if (contentToShow) { contentToShow.classList.add("active"); } if (buttonToActivate) { buttonToActivate.classList.add("active"); } // Re-build config tab each time it's opened to reflect data if (tabId === "actuarial-tab-config") { buildConfig(); } }; /** * Navigates between tabs using Next/Previous buttons * (Spec II.B.4.o) */ window.navigateActuarialTabs = function (direction) { if (!tabButtons.length) return; let currentIndex = -1; tabButtons.forEach((button, index) => { if (button.classList.contains("active")) { currentIndex = index; } }); if (currentIndex === -1) return; let nextIndex; if (direction === "next") { nextIndex = (currentIndex + 1) % tabButtons.length; } else { nextIndex = (currentIndex - 1 + tabButtons.length) % tabButtons.length; } // Get the target tabId from the button's onclick attribute const nextButton = tabButtons[nextIndex]; const onclickAttr = nextButton.getAttribute("onclick"); const tabId = onclickAttr.match(/'([^']*)'/)[1]; if (tabId) { showActuarialTab(tabId); } }; /** * Downloads the output as a PDF * (Spec II.C) */ function downloadActuarialPDF() { // Check for required libraries if ( typeof jspdf === "undefined" || typeof html2canvas === "undefined" ) { console.error( "Error: PDF generation libraries (jsPDF, html2canvas) not loaded." ); return; } if (!pdfOutputContent || !pdfOutputContent.innerHTML) { console.error("Error: PDF output element not found or empty."); return; } const { jsPDF } = jspdf; const pdfOutputElement = document.getElementById( "actuarial-pdf-output-content" ); // Temporarily make the element visible for html2canvas pdfOutputElement.style.display = "block"; pdfOutputElement.style.position = "absolute"; pdfOutputElement.style.left = "-9999px"; // Move off-screen html2canvas(pdfOutputElement, { scale: 2, // Improve resolution useCORS: true, backgroundColor: "#ffffff", }).then((canvas) => { // Hide the element again pdfOutputElement.style.display = "none"; pdfOutputElement.style.position = "static"; pdfOutputElement.style.left = "0"; const imgData = canvas.toDataURL("image/png"); const pdf = new jsPDF({ orientation: "p", unit: "px", format: "a4", }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const canvasWidth = canvas.width; const canvasHeight = canvas.height; const ratio = canvasWidth / canvasHeight; const imgWidth = pdfWidth - 40; // Add 20px margin on each side let imgHeight = imgWidth / ratio; const pageMargin = 20; if (imgHeight <= pdfHeight - 2 * pageMargin) { // Fits on one page pdf.addImage( imgData, "PNG", pageMargin, pageMargin, imgWidth, imgHeight ); } else { // Handle multiple pages let heightLeft = canvasHeight; let canvasPosition = 0; const pageHeightInPixels = (pdfHeight - 2 * pageMargin) * (canvasWidth / imgWidth); while (heightLeft > 0.1) { const pageHeight = Math.min(heightLeft, pageHeightInPixels); const pageImgHeight = (pageHeight * imgWidth) / canvasWidth; const tempCanvas = document.createElement("canvas"); tempCanvas.width = canvasWidth; tempCanvas.height = pageHeight; const ctx = tempCanvas.getContext("2d"); ctx.drawImage( canvas, 0, // sx canvasPosition, // sy canvasWidth, // sWidth pageHeight, // sHeight 0, // dx 0, // dy canvasWidth, // dWidth pageHeight // dHeight ); const pageImgData = tempCanvas.toDataURL("image/png"); pdf.addImage( pageImgData, "PNG", pageMargin, pageMargin, imgWidth, pageImgHeight ); heightLeft -= pageHeight; canvasPosition += pageHeight; if (heightLeft > 0.1) { pdf.addPage(); } } } pdf.save("actuarial-parameters.pdf"); }); } // --- Initialization --- if (pdfDownloadBtn) { pdfDownloadBtn.addEventListener("click", downloadActuarialPDF); } buildDashboard(); buildConfig(); // Show the first tab by default showActuarialTab("actuarial-tab-dashboard"); });
Scroll to Top