Spaces:
Running
Running
Add 2 files
Browse files- index.html +581 -188
- prompts.txt +2 -0
index.html
CHANGED
|
@@ -3,9 +3,10 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>
|
| 7 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
|
|
| 9 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 10 |
<style>
|
| 11 |
.slider-thumb::-webkit-slider-thumb {
|
|
@@ -71,41 +72,91 @@
|
|
| 71 |
from { opacity: 0; }
|
| 72 |
to { opacity: 1; }
|
| 73 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
</style>
|
| 75 |
</head>
|
| 76 |
<body class="bg-gray-50 min-h-screen">
|
| 77 |
<div class="container mx-auto px-4 py-8">
|
| 78 |
<header class="text-center mb-12">
|
| 79 |
-
<h1 class="text-4xl font-bold text-blue-800 mb-4">
|
| 80 |
<p class="text-lg text-gray-600 max-w-3xl mx-auto">
|
| 81 |
-
|
|
|
|
| 82 |
</p>
|
| 83 |
</header>
|
| 84 |
|
| 85 |
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
| 86 |
<!-- Input Controls -->
|
| 87 |
<div class="bg-white rounded-xl shadow-lg p-6 lg:col-span-1">
|
| 88 |
-
<h2 class="text-2xl font-semibold text-blue-700 mb-6">
|
| 89 |
|
| 90 |
<div class="space-y-6">
|
| 91 |
<div>
|
| 92 |
<label for="salary" class="block text-sm font-medium text-gray-700 mb-2">
|
| 93 |
-
|
| 94 |
<span class="tooltip ml-1">
|
| 95 |
<i class="fas fa-info-circle text-blue-500"></i>
|
| 96 |
-
<span class="tooltiptext">Din bruttolön innan skatt och
|
| 97 |
</span>
|
| 98 |
</label>
|
| 99 |
<div class="flex items-center">
|
| 100 |
<span class="mr-2 text-gray-500">kr</span>
|
| 101 |
-
<input type="range" id="salary" min="20000" max="
|
| 102 |
<span id="salaryValue" class="ml-4 font-medium">45 000</span>
|
| 103 |
</div>
|
| 104 |
</div>
|
| 105 |
|
| 106 |
<div>
|
| 107 |
<label for="age" class="block text-sm font-medium text-gray-700 mb-2">
|
| 108 |
-
|
| 109 |
<span class="tooltip ml-1">
|
| 110 |
<i class="fas fa-info-circle text-blue-500"></i>
|
| 111 |
<span class="tooltiptext">Din nuvarande ålder för att beräkna tills pension</span>
|
|
@@ -116,6 +167,24 @@
|
|
| 116 |
<span id="ageValue" class="ml-4 font-medium">35</span>
|
| 117 |
</div>
|
| 118 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
<div>
|
| 121 |
<label for="retirementAge" class="block text-sm font-medium text-gray-700 mb-2">
|
|
@@ -131,6 +200,76 @@
|
|
| 131 |
</div>
|
| 132 |
</div>
|
| 133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
<div>
|
| 135 |
<label for="pensionContribution" class="block text-sm font-medium text-gray-700 mb-2">
|
| 136 |
Pensionsavsättning (% av lön)
|
|
@@ -175,7 +314,7 @@
|
|
| 175 |
|
| 176 |
<div class="pt-4">
|
| 177 |
<button id="calculateBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition duration-200 flex items-center justify-center">
|
| 178 |
-
<i class="fas fa-calculator mr-2"></i> Beräkna
|
| 179 |
</button>
|
| 180 |
</div>
|
| 181 |
</div>
|
|
@@ -183,33 +322,49 @@
|
|
| 183 |
|
| 184 |
<!-- Results Visualization -->
|
| 185 |
<div class="bg-white rounded-xl shadow-lg p-6 lg:col-span-2">
|
| 186 |
-
<h2 class="text-2xl font-semibold text-blue-700 mb-6">
|
| 187 |
|
| 188 |
-
<div class="grid grid-cols-1 md:grid-cols-
|
| 189 |
<div class="bg-blue-50 rounded-lg p-4 border border-blue-100">
|
| 190 |
-
<h3 class="text-lg font-medium text-blue-800 mb-2">
|
| 191 |
-
<p class="text-
|
| 192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
</div>
|
| 194 |
|
| 195 |
<div class="bg-green-50 rounded-lg p-4 border border-green-100">
|
| 196 |
-
<h3 class="text-lg font-medium text-green-800 mb-2">
|
| 197 |
-
<p class="text-
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
</div>
|
| 200 |
</div>
|
| 201 |
|
| 202 |
<div class="mb-8">
|
| 203 |
-
<h3 class="text-xl font-semibold text-gray-800 mb-4">
|
| 204 |
<div class="h-64">
|
| 205 |
-
<canvas id="
|
| 206 |
</div>
|
| 207 |
</div>
|
| 208 |
|
| 209 |
<div class="mb-8">
|
| 210 |
-
<h3 class="text-xl font-semibold text-gray-800 mb-4">Pensionsutveckling
|
| 211 |
<div class="h-64">
|
| 212 |
-
<canvas id="
|
| 213 |
</div>
|
| 214 |
</div>
|
| 215 |
|
|
@@ -219,19 +374,26 @@
|
|
| 219 |
<i class="fas fa-lightbulb text-yellow-500 text-xl"></i>
|
| 220 |
</div>
|
| 221 |
<div class="ml-3">
|
| 222 |
-
<h3 class="text-sm font-medium text-yellow-800">
|
| 223 |
-
<div class="mt-2 text-sm text-yellow-700" id="
|
| 224 |
-
<p>
|
| 225 |
</div>
|
| 226 |
</div>
|
| 227 |
</div>
|
| 228 |
</div>
|
| 229 |
|
| 230 |
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
| 231 |
-
<h3 class="text-lg font-medium text-gray-800 mb-3">
|
| 232 |
-
<div class="space-y-3 text-sm text-gray-700" id="
|
| 233 |
-
<p>
|
| 234 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
</div>
|
| 236 |
</div>
|
| 237 |
</div>
|
|
@@ -239,16 +401,39 @@
|
|
| 239 |
</div>
|
| 240 |
|
| 241 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
// Format numbers with spaces as thousand separators
|
| 243 |
function formatNumber(num) {
|
| 244 |
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
| 245 |
}
|
| 246 |
|
| 247 |
-
// Calculate tax
|
| 248 |
-
function
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
}
|
| 253 |
|
| 254 |
// Calculate employer contributions (simplified)
|
|
@@ -256,112 +441,239 @@
|
|
| 256 |
return salary * 0.3142; // Approximate employer contributions in Sweden
|
| 257 |
}
|
| 258 |
|
| 259 |
-
// Calculate compound interest
|
| 260 |
-
function calculateCompoundInterest(principal, annualRate, years,
|
| 261 |
let amount = principal;
|
| 262 |
const monthlyRate = annualRate / 12 / 100;
|
| 263 |
const months = years * 12;
|
|
|
|
| 264 |
|
| 265 |
for (let i = 0; i < months; i++) {
|
| 266 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
}
|
| 268 |
|
| 269 |
return amount;
|
| 270 |
}
|
| 271 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
// Update all displayed values when inputs change
|
| 273 |
function updateDisplayValues() {
|
| 274 |
document.getElementById('salaryValue').textContent = formatNumber(document.getElementById('salary').value);
|
| 275 |
document.getElementById('ageValue').textContent = document.getElementById('age').value;
|
|
|
|
| 276 |
document.getElementById('retirementAgeValue').textContent = document.getElementById('retirementAge').value;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
document.getElementById('pensionContributionValue').textContent = document.getElementById('pensionContribution').value + '%';
|
| 278 |
document.getElementById('salaryExchangeValue').textContent = document.getElementById('salaryExchange').value + '%';
|
| 279 |
document.getElementById('expectedReturnValue').textContent = document.getElementById('expectedReturn').value + '%';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
}
|
| 281 |
|
| 282 |
// Initialize charts
|
| 283 |
-
let
|
| 284 |
-
let
|
| 285 |
|
| 286 |
function initCharts() {
|
| 287 |
-
const
|
| 288 |
-
|
| 289 |
-
type: '
|
| 290 |
data: {
|
| 291 |
-
labels: [
|
| 292 |
-
datasets: [
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
'#
|
| 297 |
-
'
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
|
|
|
|
|
|
| 301 |
},
|
| 302 |
options: {
|
| 303 |
responsive: true,
|
| 304 |
maintainAspectRatio: false,
|
| 305 |
plugins: {
|
| 306 |
-
legend: {
|
| 307 |
-
position: 'right',
|
| 308 |
-
},
|
| 309 |
tooltip: {
|
| 310 |
callbacks: {
|
| 311 |
label: function(context) {
|
| 312 |
-
|
| 313 |
-
if (label) {
|
| 314 |
-
label += ': ';
|
| 315 |
-
}
|
| 316 |
-
label += formatNumber(context.raw) + ' kr';
|
| 317 |
-
return label;
|
| 318 |
}
|
| 319 |
}
|
| 320 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
}
|
| 322 |
}
|
| 323 |
});
|
| 324 |
|
| 325 |
-
const
|
| 326 |
-
|
| 327 |
-
type: '
|
| 328 |
data: {
|
| 329 |
-
labels: [],
|
| 330 |
-
datasets: [
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
},
|
| 340 |
-
{
|
| 341 |
-
label: 'Utan löneväxling',
|
| 342 |
-
data: [],
|
| 343 |
-
borderColor: '#64748b',
|
| 344 |
-
backgroundColor: 'rgba(100, 116, 139, 0.1)',
|
| 345 |
-
borderWidth: 2,
|
| 346 |
-
borderDash: [5, 5],
|
| 347 |
-
fill: true,
|
| 348 |
-
tension: 0.4
|
| 349 |
-
}
|
| 350 |
-
]
|
| 351 |
},
|
| 352 |
options: {
|
| 353 |
responsive: true,
|
| 354 |
maintainAspectRatio: false,
|
| 355 |
plugins: {
|
|
|
|
|
|
|
|
|
|
| 356 |
tooltip: {
|
| 357 |
callbacks: {
|
| 358 |
label: function(context) {
|
| 359 |
-
|
| 360 |
-
if (label) {
|
| 361 |
-
label += ': ';
|
| 362 |
-
}
|
| 363 |
-
label += formatNumber(context.raw) + ' kr';
|
| 364 |
-
return label;
|
| 365 |
}
|
| 366 |
}
|
| 367 |
}
|
|
@@ -380,130 +692,211 @@
|
|
| 380 |
});
|
| 381 |
}
|
| 382 |
|
| 383 |
-
//
|
| 384 |
-
function
|
| 385 |
// Get input values
|
| 386 |
const salary = parseInt(document.getElementById('salary').value);
|
|
|
|
| 387 |
const age = parseInt(document.getElementById('age').value);
|
| 388 |
const retirementAge = parseInt(document.getElementById('retirementAge').value);
|
| 389 |
-
const
|
| 390 |
-
const
|
|
|
|
|
|
|
|
|
|
| 391 |
const expectedReturn = parseFloat(document.getElementById('expectedReturn').value);
|
| 392 |
|
| 393 |
-
// Calculate years to retirement
|
| 394 |
const yearsToRetirement = retirementAge - age;
|
|
|
|
| 395 |
|
| 396 |
-
//
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
// Calculate tax savings from salary exchange
|
| 402 |
-
const taxRate = calculateTax(salary) / salary;
|
| 403 |
-
const employerContributionRate = 0.3142; // Approximate employer contributions in Sweden
|
| 404 |
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
|
| 419 |
-
|
| 420 |
-
|
|
|
|
|
|
|
| 421 |
|
| 422 |
-
// Update
|
| 423 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
document.getElementById('salaryChange').className = 'text-sm text-red-600 mt-1';
|
| 432 |
-
} else {
|
| 433 |
-
document.getElementById('salaryChange').textContent = 'Ingen förändring';
|
| 434 |
-
document.getElementById('salaryChange').className = 'text-sm text-gray-600 mt-1';
|
| 435 |
-
}
|
| 436 |
|
| 437 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 446 |
} else {
|
| 447 |
-
|
| 448 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
}
|
| 450 |
|
| 451 |
-
//
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
Math.round(taxSaved),
|
| 455 |
-
Math.round(netSalaryWithExchange)
|
| 456 |
-
];
|
| 457 |
-
contributionChart.update();
|
| 458 |
-
|
| 459 |
-
// Update growth chart
|
| 460 |
-
const labels = [];
|
| 461 |
-
const withExchangeData = [];
|
| 462 |
-
const withoutExchangeData = [];
|
| 463 |
-
|
| 464 |
-
for (let year = 0; year <= yearsToRetirement; year++) {
|
| 465 |
-
labels.push(`År ${year}`);
|
| 466 |
-
|
| 467 |
-
const withExchange = calculateCompoundInterest(0, expectedReturn, year, monthlyContributionWithExchange);
|
| 468 |
-
const withoutExchange = calculateCompoundInterest(0, expectedReturn, year, monthlyContributionWithoutExchange);
|
| 469 |
-
|
| 470 |
-
withExchangeData.push(Math.round(withExchange));
|
| 471 |
-
withoutExchangeData.push(Math.round(withoutExchange));
|
| 472 |
}
|
| 473 |
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
growthChart.data.datasets[1].data = withoutExchangeData;
|
| 477 |
-
growthChart.update();
|
| 478 |
-
|
| 479 |
-
// Update optimization tips
|
| 480 |
-
let tips = '';
|
| 481 |
-
if (salaryExchange === 0) {
|
| 482 |
-
tips = 'Du använder inte löneväxling alls. Öka löneväxlingsandelen för att se potentiella skattebesparingar och högre pensionsavsättningar.';
|
| 483 |
-
} else if (salaryExchange < 50) {
|
| 484 |
-
tips = 'Du löneväxlar en del av din pensionsavsättning. Öka andelen för större skattebesparingar och högre pensionsavsättningar.';
|
| 485 |
-
} else if (salaryExchange < 100) {
|
| 486 |
-
tips = 'Du löneväxlar en stor del av din pensionsavsättning. Öka till 100% för maximala skattebesparingar.';
|
| 487 |
-
} else {
|
| 488 |
-
tips = 'Du löneväxlar hela din pensionsavsättning. Detta ger maximala skattebesparingar och pensionsavsättningar.';
|
| 489 |
}
|
| 490 |
|
| 491 |
-
if (
|
| 492 |
-
tips += '
|
| 493 |
-
} else if (yearsToRetirement > 30) {
|
| 494 |
-
tips += ' Med många år kvar till pension har ditt sparande lång tid att växa - löneväxling kan ge betydande fördelar över tid.';
|
| 495 |
}
|
| 496 |
|
| 497 |
-
document.getElementById('
|
| 498 |
|
| 499 |
// Add animation classes
|
| 500 |
-
document.getElementById('
|
|
|
|
| 501 |
document.getElementById('totalPension').classList.add('animate-grow');
|
| 502 |
-
document.querySelectorAll('.tooltip').forEach(el => el.classList.add('fade-in'));
|
| 503 |
|
| 504 |
// Remove animation classes after animation completes
|
| 505 |
setTimeout(() => {
|
| 506 |
-
document.getElementById('
|
|
|
|
| 507 |
document.getElementById('totalPension').classList.remove('animate-grow');
|
| 508 |
}, 1500);
|
| 509 |
}
|
|
@@ -519,7 +912,7 @@
|
|
| 519 |
|
| 520 |
// Set up calculate button
|
| 521 |
document.getElementById('calculateBtn').addEventListener('click', function() {
|
| 522 |
-
|
| 523 |
});
|
| 524 |
|
| 525 |
// Initialize display values and charts
|
|
@@ -527,7 +920,7 @@
|
|
| 527 |
initCharts();
|
| 528 |
|
| 529 |
// Calculate initial results
|
| 530 |
-
|
| 531 |
});
|
| 532 |
</script>
|
| 533 |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Jausing/misc-stuff" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Thomanders Pensionsoptimering</title>
|
| 7 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
| 9 |
+
<script src="https://cdn.jsdelivr.net/npm/slider-ui/dist/slider-ui.min.js"></script>
|
| 10 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 11 |
<style>
|
| 12 |
.slider-thumb::-webkit-slider-thumb {
|
|
|
|
| 72 |
from { opacity: 0; }
|
| 73 |
to { opacity: 1; }
|
| 74 |
}
|
| 75 |
+
|
| 76 |
+
.progress-bar {
|
| 77 |
+
height: 8px;
|
| 78 |
+
border-radius: 4px;
|
| 79 |
+
background-color: #e5e7eb;
|
| 80 |
+
overflow: hidden;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
.progress-bar-fill {
|
| 84 |
+
height: 100%;
|
| 85 |
+
background-color: #3b82f6;
|
| 86 |
+
transition: width 0.3s ease;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
.function-graph {
|
| 90 |
+
height: 200px;
|
| 91 |
+
width: 100%;
|
| 92 |
+
background-color: #f8fafc;
|
| 93 |
+
border-radius: 8px;
|
| 94 |
+
position: relative;
|
| 95 |
+
overflow: hidden;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
.graph-line {
|
| 99 |
+
position: absolute;
|
| 100 |
+
bottom: 0;
|
| 101 |
+
left: 0;
|
| 102 |
+
width: 100%;
|
| 103 |
+
height: 2px;
|
| 104 |
+
background-color: #3b82f6;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
.tax-bracket {
|
| 108 |
+
position: absolute;
|
| 109 |
+
bottom: 0;
|
| 110 |
+
height: 100%;
|
| 111 |
+
background-color: rgba(59, 130, 246, 0.1);
|
| 112 |
+
border-left: 1px dashed #3b82f6;
|
| 113 |
+
border-right: 1px dashed #3b82f6;
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
.tax-bracket-label {
|
| 117 |
+
position: absolute;
|
| 118 |
+
top: 5px;
|
| 119 |
+
transform: translateX(-50%);
|
| 120 |
+
font-size: 10px;
|
| 121 |
+
color: #3b82f6;
|
| 122 |
+
white-space: nowrap;
|
| 123 |
+
}
|
| 124 |
</style>
|
| 125 |
</head>
|
| 126 |
<body class="bg-gray-50 min-h-screen">
|
| 127 |
<div class="container mx-auto px-4 py-8">
|
| 128 |
<header class="text-center mb-12">
|
| 129 |
+
<h1 class="text-4xl font-bold text-blue-800 mb-4">Thomanders Pensionsoptimering - Livscykelvärdering</h1>
|
| 130 |
<p class="text-lg text-gray-600 max-w-3xl mx-auto">
|
| 131 |
+
Vad är en finansiell rådgivare från Max värd? Ungefär en prompt....En komplett modell för att maximera din upplevda livskvalitet genom optimal pensionsplanering.
|
| 132 |
+
Tar hänsyn till skiktgränser, subjektiv tidspreferens och osäkerhet i livslängd.
|
| 133 |
</p>
|
| 134 |
</header>
|
| 135 |
|
| 136 |
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
| 137 |
<!-- Input Controls -->
|
| 138 |
<div class="bg-white rounded-xl shadow-lg p-6 lg:col-span-1">
|
| 139 |
+
<h2 class="text-2xl font-semibold text-blue-700 mb-6">Hårda Parametrar</h2>
|
| 140 |
|
| 141 |
<div class="space-y-6">
|
| 142 |
<div>
|
| 143 |
<label for="salary" class="block text-sm font-medium text-gray-700 mb-2">
|
| 144 |
+
Nuvarande månadslön (brutto)
|
| 145 |
<span class="tooltip ml-1">
|
| 146 |
<i class="fas fa-info-circle text-blue-500"></i>
|
| 147 |
+
<span class="tooltiptext">Din nuvarande bruttolön innan skatt och avdrag</span>
|
| 148 |
</span>
|
| 149 |
</label>
|
| 150 |
<div class="flex items-center">
|
| 151 |
<span class="mr-2 text-gray-500">kr</span>
|
| 152 |
+
<input type="range" id="salary" min="20000" max="150000" step="1000" value="45000" class="w-full h-2 bg-gray-200 rounded-lg slider-thumb">
|
| 153 |
<span id="salaryValue" class="ml-4 font-medium">45 000</span>
|
| 154 |
</div>
|
| 155 |
</div>
|
| 156 |
|
| 157 |
<div>
|
| 158 |
<label for="age" class="block text-sm font-medium text-gray-700 mb-2">
|
| 159 |
+
Nuvarande ålder
|
| 160 |
<span class="tooltip ml-1">
|
| 161 |
<i class="fas fa-info-circle text-blue-500"></i>
|
| 162 |
<span class="tooltiptext">Din nuvarande ålder för att beräkna tills pension</span>
|
|
|
|
| 167 |
<span id="ageValue" class="ml-4 font-medium">35</span>
|
| 168 |
</div>
|
| 169 |
</div>
|
| 170 |
+
</div>
|
| 171 |
+
|
| 172 |
+
<h2 class="text-2xl font-semibold text-blue-700 mt-8 mb-6">Semi-flexibla Parametrar</h2>
|
| 173 |
+
|
| 174 |
+
<div class="space-y-6">
|
| 175 |
+
<div>
|
| 176 |
+
<label for="salaryGrowth" class="block text-sm font-medium text-gray-700 mb-2">
|
| 177 |
+
Förväntad årlig löneutveckling (%)
|
| 178 |
+
<span class="tooltip ml-1">
|
| 179 |
+
<i class="fas fa-info-circle text-blue-500"></i>
|
| 180 |
+
<span class="tooltiptext">Genomsnittlig årlig ökning av din lön fram till pension</span>
|
| 181 |
+
</span>
|
| 182 |
+
</label>
|
| 183 |
+
<div class="flex items-center">
|
| 184 |
+
<input type="range" id="salaryGrowth" min="-2" max="10" step="0.5" value="2.5" class="w-full h-2 bg-gray-200 rounded-lg slider-thumb">
|
| 185 |
+
<span id="salaryGrowthValue" class="ml-4 font-medium">2.5%</span>
|
| 186 |
+
</div>
|
| 187 |
+
</div>
|
| 188 |
|
| 189 |
<div>
|
| 190 |
<label for="retirementAge" class="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
| 200 |
</div>
|
| 201 |
</div>
|
| 202 |
|
| 203 |
+
<div>
|
| 204 |
+
<label for="lifeExpectancy" class="block text-sm font-medium text-gray-700 mb-2">
|
| 205 |
+
Förväntad dödsålder
|
| 206 |
+
<span class="tooltip ml-1">
|
| 207 |
+
<i class="fas fa-info-circle text-blue-500"></i>
|
| 208 |
+
<span class="tooltiptext">Ålder då du förväntar dig att dö (baserat på släkt och hälsa)</span>
|
| 209 |
+
</span>
|
| 210 |
+
</label>
|
| 211 |
+
<div class="flex items-center">
|
| 212 |
+
<input type="range" id="lifeExpectancy" min="65" max="100" step="1" value="85" class="w-full h-2 bg-gray-200 rounded-lg slider-thumb">
|
| 213 |
+
<span id="lifeExpectancyValue" class="ml-4 font-medium">85</span>
|
| 214 |
+
</div>
|
| 215 |
+
</div>
|
| 216 |
+
</div>
|
| 217 |
+
|
| 218 |
+
<h2 class="text-2xl font-semibold text-blue-700 mt-8 mb-6">Subjektiva Värderingar</h2>
|
| 219 |
+
|
| 220 |
+
<div class="space-y-6">
|
| 221 |
+
<div>
|
| 222 |
+
<label for="currentValueMultiplier" class="block text-sm font-medium text-gray-700 mb-2">
|
| 223 |
+
Nuvarande värdefaktor (300% betyder 3x mer värde idag)
|
| 224 |
+
<span class="tooltip ml-1">
|
| 225 |
+
<i class="fas fa-info-circle text-blue-500"></i>
|
| 226 |
+
<span class="tooltiptext">Hur mycket mer värderar du pengar idag jämfört med efter pension?</span>
|
| 227 |
+
</span>
|
| 228 |
+
</label>
|
| 229 |
+
<div class="flex items-center">
|
| 230 |
+
<input type="range" id="currentValueMultiplier" min="100" max="500" step="10" value="300" class="w-full h-2 bg-gray-200 rounded-lg slider-thumb">
|
| 231 |
+
<span id="currentValueMultiplierValue" class="ml-4 font-medium">300%</span>
|
| 232 |
+
</div>
|
| 233 |
+
<div class="function-graph mt-2" id="currentValueGraph">
|
| 234 |
+
<div class="graph-line"></div>
|
| 235 |
+
</div>
|
| 236 |
+
</div>
|
| 237 |
+
|
| 238 |
+
<div>
|
| 239 |
+
<label for="retirementValueMultiplier" class="block text-sm font-medium text-gray-700 mb-2">
|
| 240 |
+
Pensionsvärdefaktor (100% = neutral)
|
| 241 |
+
<span class="tooltip ml-1">
|
| 242 |
+
<i class="fas fa-info-circle text-blue-500"></i>
|
| 243 |
+
<span class="tooltiptext">Hur mycket värderar du pengar under pensionsåren jämfört med vid pensionsstart?</span>
|
| 244 |
+
</span>
|
| 245 |
+
</label>
|
| 246 |
+
<div class="flex items-center">
|
| 247 |
+
<input type="range" id="retirementValueMultiplier" min="0" max="200" step="5" value="100" class="w-full h-2 bg-gray-200 rounded-lg slider-thumb">
|
| 248 |
+
<span id="retirementValueMultiplierValue" class="ml-4 font-medium">100%</span>
|
| 249 |
+
</div>
|
| 250 |
+
<div class="function-graph mt-2" id="retirementValueGraph">
|
| 251 |
+
<div class="graph-line"></div>
|
| 252 |
+
</div>
|
| 253 |
+
</div>
|
| 254 |
+
|
| 255 |
+
<div>
|
| 256 |
+
<label for="endOfLifeReserve" class="block text-sm font-medium text-gray-700 mb-2">
|
| 257 |
+
Reserv vid dödsålder (% av totalt sparande)
|
| 258 |
+
<span class="tooltip ml-1">
|
| 259 |
+
<i class="fas fa-info-circle text-blue-500"></i>
|
| 260 |
+
<span class="tooltiptext">Hur stor del av ditt sparande vill du ha kvar vid förväntad dödsålder?</span>
|
| 261 |
+
</span>
|
| 262 |
+
</label>
|
| 263 |
+
<div class="flex items-center">
|
| 264 |
+
<input type="range" id="endOfLifeReserve" min="0" max="100" step="5" value="20" class="w-full h-2 bg-gray-200 rounded-lg slider-thumb">
|
| 265 |
+
<span id="endOfLifeReserveValue" class="ml-4 font-medium">20%</span>
|
| 266 |
+
</div>
|
| 267 |
+
</div>
|
| 268 |
+
</div>
|
| 269 |
+
|
| 270 |
+
<h2 class="text-2xl font-semibold text-blue-700 mt-8 mb-6">Pensionsstrategi</h2>
|
| 271 |
+
|
| 272 |
+
<div class="space-y-6">
|
| 273 |
<div>
|
| 274 |
<label for="pensionContribution" class="block text-sm font-medium text-gray-700 mb-2">
|
| 275 |
Pensionsavsättning (% av lön)
|
|
|
|
| 314 |
|
| 315 |
<div class="pt-4">
|
| 316 |
<button id="calculateBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition duration-200 flex items-center justify-center">
|
| 317 |
+
<i class="fas fa-calculator mr-2"></i> Beräkna Optimal Strategi
|
| 318 |
</button>
|
| 319 |
</div>
|
| 320 |
</div>
|
|
|
|
| 322 |
|
| 323 |
<!-- Results Visualization -->
|
| 324 |
<div class="bg-white rounded-xl shadow-lg p-6 lg:col-span-2">
|
| 325 |
+
<h2 class="text-2xl font-semibold text-blue-700 mb-6">Optimeringsresultat</h2>
|
| 326 |
|
| 327 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
| 328 |
<div class="bg-blue-50 rounded-lg p-4 border border-blue-100">
|
| 329 |
+
<h3 class="text-lg font-medium text-blue-800 mb-2">Optimal löneväxling</h3>
|
| 330 |
+
<p class="text-2xl font-bold text-blue-600" id="optimalExchange">0%</p>
|
| 331 |
+
</div>
|
| 332 |
+
|
| 333 |
+
<div class="bg-purple-50 rounded-lg p-4 border border-purple-100">
|
| 334 |
+
<h3 class="text-lg font-medium text-purple-800 mb-2">Maximerat livsvärde</h3>
|
| 335 |
+
<p class="text-2xl font-bold text-purple-600" id="maxValue">0</p>
|
| 336 |
</div>
|
| 337 |
|
| 338 |
<div class="bg-green-50 rounded-lg p-4 border border-green-100">
|
| 339 |
+
<h3 class="text-lg font-medium text-green-800 mb-2">Pensionspot vid pension</h3>
|
| 340 |
+
<p class="text-2xl font-bold text-green-600" id="totalPension">0 kr</p>
|
| 341 |
+
</div>
|
| 342 |
+
</div>
|
| 343 |
+
|
| 344 |
+
<div class="mb-6">
|
| 345 |
+
<h3 class="text-xl font-semibold text-gray-800 mb-3">Skatteskikt och Marginaleffekt</h3>
|
| 346 |
+
<div class="bg-white border border-gray-200 rounded-lg p-4">
|
| 347 |
+
<div class="function-graph" id="taxBracketGraph">
|
| 348 |
+
<div class="graph-line"></div>
|
| 349 |
+
<!-- Tax brackets will be added dynamically -->
|
| 350 |
+
</div>
|
| 351 |
+
<div class="mt-2 text-sm text-gray-600" id="marginalTaxText">
|
| 352 |
+
Din marginaleffekt vid löneväxling beräknas baserat på dina parametrar.
|
| 353 |
+
</div>
|
| 354 |
</div>
|
| 355 |
</div>
|
| 356 |
|
| 357 |
<div class="mb-8">
|
| 358 |
+
<h3 class="text-xl font-semibold text-gray-800 mb-4">Livscykelvärde över tid</h3>
|
| 359 |
<div class="h-64">
|
| 360 |
+
<canvas id="lifecycleChart"></canvas>
|
| 361 |
</div>
|
| 362 |
</div>
|
| 363 |
|
| 364 |
<div class="mb-8">
|
| 365 |
+
<h3 class="text-xl font-semibold text-gray-800 mb-4">Pensionsutveckling med optimal strategi</h3>
|
| 366 |
<div class="h-64">
|
| 367 |
+
<canvas id="pensionChart"></canvas>
|
| 368 |
</div>
|
| 369 |
</div>
|
| 370 |
|
|
|
|
| 374 |
<i class="fas fa-lightbulb text-yellow-500 text-xl"></i>
|
| 375 |
</div>
|
| 376 |
<div class="ml-3">
|
| 377 |
+
<h3 class="text-sm font-medium text-yellow-800">Optimeringsstrategi</h3>
|
| 378 |
+
<div class="mt-2 text-sm text-yellow-700" id="strategyTips">
|
| 379 |
+
<p>Systemet beräknar den optimala löneväxlingsnivån baserat på dina parametrar och subjektiva värderingar.</p>
|
| 380 |
</div>
|
| 381 |
</div>
|
| 382 |
</div>
|
| 383 |
</div>
|
| 384 |
|
| 385 |
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
| 386 |
+
<h3 class="text-lg font-medium text-gray-800 mb-3">Livscykelanalys</h3>
|
| 387 |
+
<div class="space-y-3 text-sm text-gray-700" id="lifecycleAnalysis">
|
| 388 |
+
<p>Denna optimering tar hänsyn till:</p>
|
| 389 |
+
<ul class="list-disc pl-5 space-y-1">
|
| 390 |
+
<li>Skatteskikt och marginaleffekter av löneväxling</li>
|
| 391 |
+
<li>Din subjektiva värdering av pengar över tid</li>
|
| 392 |
+
<li>Förväntad löneutveckling och livslängd</li>
|
| 393 |
+
<li>Avkastning på pensionssparande</li>
|
| 394 |
+
<li>Önskad reserv vid förväntad dödsålder</li>
|
| 395 |
+
</ul>
|
| 396 |
+
<p>Resultatet visar den strategi som maximerar din upplevda livskvalitet över hela livscykeln.</p>
|
| 397 |
</div>
|
| 398 |
</div>
|
| 399 |
</div>
|
|
|
|
| 401 |
</div>
|
| 402 |
|
| 403 |
<script>
|
| 404 |
+
// Swedish tax brackets for 2023 (simplified)
|
| 405 |
+
const TAX_BRACKETS = [
|
| 406 |
+
{ min: 0, max: 509300, rate: 0.0, name: "Ingen skatt" },
|
| 407 |
+
{ min: 509300, max: 673400, rate: 0.2, name: "Kommunal skatt" },
|
| 408 |
+
{ min: 673400, max: 1018600, rate: 0.5, name: "Statlig skatt" },
|
| 409 |
+
{ min: 1018600, max: Infinity, rate: 0.55, name: "Toppskatt" }
|
| 410 |
+
];
|
| 411 |
+
|
| 412 |
// Format numbers with spaces as thousand separators
|
| 413 |
function formatNumber(num) {
|
| 414 |
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
|
| 415 |
}
|
| 416 |
|
| 417 |
+
// Calculate marginal tax rate for a given salary
|
| 418 |
+
function calculateMarginalTax(salary) {
|
| 419 |
+
for (let i = TAX_BRACKETS.length - 1; i >= 0; i--) {
|
| 420 |
+
if (salary > TAX_BRACKETS[i].min) {
|
| 421 |
+
return TAX_BRACKETS[i].rate;
|
| 422 |
+
}
|
| 423 |
+
}
|
| 424 |
+
return 0;
|
| 425 |
+
}
|
| 426 |
+
|
| 427 |
+
// Calculate total tax for a given salary
|
| 428 |
+
function calculateTotalTax(salary) {
|
| 429 |
+
let tax = 0;
|
| 430 |
+
for (const bracket of TAX_BRACKETS) {
|
| 431 |
+
if (salary > bracket.min) {
|
| 432 |
+
const taxableAmount = Math.min(salary, bracket.max) - bracket.min;
|
| 433 |
+
tax += taxableAmount * bracket.rate;
|
| 434 |
+
}
|
| 435 |
+
}
|
| 436 |
+
return tax;
|
| 437 |
}
|
| 438 |
|
| 439 |
// Calculate employer contributions (simplified)
|
|
|
|
| 441 |
return salary * 0.3142; // Approximate employer contributions in Sweden
|
| 442 |
}
|
| 443 |
|
| 444 |
+
// Calculate compound interest with growing contributions
|
| 445 |
+
function calculateCompoundInterest(principal, annualRate, years, initialMonthlyContribution, contributionGrowthRate) {
|
| 446 |
let amount = principal;
|
| 447 |
const monthlyRate = annualRate / 12 / 100;
|
| 448 |
const months = years * 12;
|
| 449 |
+
let currentContribution = initialMonthlyContribution;
|
| 450 |
|
| 451 |
for (let i = 0; i < months; i++) {
|
| 452 |
+
// Apply annual contribution growth (every 12 months)
|
| 453 |
+
if (i > 0 && i % 12 === 0) {
|
| 454 |
+
currentContribution = currentContribution * (1 + contributionGrowthRate / 100);
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
amount = amount * (1 + monthlyRate) + currentContribution;
|
| 458 |
}
|
| 459 |
|
| 460 |
return amount;
|
| 461 |
}
|
| 462 |
|
| 463 |
+
// Calculate projected salary with growth
|
| 464 |
+
function calculateProjectedSalary(currentSalary, growthRate, years) {
|
| 465 |
+
return currentSalary * Math.pow(1 + growthRate / 100, years);
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
// Calculate time value multiplier (current to retirement)
|
| 469 |
+
function calculateCurrentValueMultiplier(yearsToRetirement, currentMultiplier) {
|
| 470 |
+
// Linear interpolation from currentMultiplier to 100% at retirement
|
| 471 |
+
return 100 + (currentMultiplier - 100) * (1 - yearsToRetirement / (retirementAge - age));
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
// Calculate time value multiplier (retirement to death)
|
| 475 |
+
function calculateRetirementValueMultiplier(yearsFromRetirement, retirementMultiplier) {
|
| 476 |
+
// Linear interpolation from 100% to retirementMultiplier at death
|
| 477 |
+
return 100 + (retirementMultiplier - 100) * (yearsFromRetirement / (lifeExpectancy - retirementAge));
|
| 478 |
+
}
|
| 479 |
+
|
| 480 |
// Update all displayed values when inputs change
|
| 481 |
function updateDisplayValues() {
|
| 482 |
document.getElementById('salaryValue').textContent = formatNumber(document.getElementById('salary').value);
|
| 483 |
document.getElementById('ageValue').textContent = document.getElementById('age').value;
|
| 484 |
+
document.getElementById('salaryGrowthValue').textContent = document.getElementById('salaryGrowth').value + '%';
|
| 485 |
document.getElementById('retirementAgeValue').textContent = document.getElementById('retirementAge').value;
|
| 486 |
+
document.getElementById('lifeExpectancyValue').textContent = document.getElementById('lifeExpectancy').value;
|
| 487 |
+
document.getElementById('currentValueMultiplierValue').textContent = document.getElementById('currentValueMultiplier').value + '%';
|
| 488 |
+
document.getElementById('retirementValueMultiplierValue').textContent = document.getElementById('retirementValueMultiplier').value + '%';
|
| 489 |
+
document.getElementById('endOfLifeReserveValue').textContent = document.getElementById('endOfLifeReserve').value + '%';
|
| 490 |
document.getElementById('pensionContributionValue').textContent = document.getElementById('pensionContribution').value + '%';
|
| 491 |
document.getElementById('salaryExchangeValue').textContent = document.getElementById('salaryExchange').value + '%';
|
| 492 |
document.getElementById('expectedReturnValue').textContent = document.getElementById('expectedReturn').value + '%';
|
| 493 |
+
|
| 494 |
+
updateValueGraphs();
|
| 495 |
+
updateTaxBracketGraph();
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
// Update the value function graphs
|
| 499 |
+
function updateValueGraphs() {
|
| 500 |
+
const currentMultiplier = parseInt(document.getElementById('currentValueMultiplier').value);
|
| 501 |
+
const retirementMultiplier = parseInt(document.getElementById('retirementValueMultiplier').value);
|
| 502 |
+
const age = parseInt(document.getElementById('age').value);
|
| 503 |
+
const retirementAge = parseInt(document.getElementById('retirementAge').value);
|
| 504 |
+
const lifeExpectancy = parseInt(document.getElementById('lifeExpectancy').value);
|
| 505 |
+
|
| 506 |
+
// Current value graph (from now to retirement)
|
| 507 |
+
const currentGraph = document.getElementById('currentValueGraph');
|
| 508 |
+
currentGraph.innerHTML = '<div class="graph-line"></div>';
|
| 509 |
+
|
| 510 |
+
// Add points to show the value decay
|
| 511 |
+
for (let y = age; y <= retirementAge; y++) {
|
| 512 |
+
const yearsToRetirement = retirementAge - y;
|
| 513 |
+
const valuePercent = calculateCurrentValueMultiplier(yearsToRetirement, currentMultiplier);
|
| 514 |
+
const point = document.createElement('div');
|
| 515 |
+
point.style.position = 'absolute';
|
| 516 |
+
point.style.bottom = '0';
|
| 517 |
+
point.style.left = `${((y - age) / (retirementAge - age)) * 100}%`;
|
| 518 |
+
point.style.width = '2px';
|
| 519 |
+
point.style.height = `${Math.min(100, valuePercent)}%`;
|
| 520 |
+
point.style.backgroundColor = '#3b82f6';
|
| 521 |
+
currentGraph.appendChild(point);
|
| 522 |
+
}
|
| 523 |
+
|
| 524 |
+
// Retirement value graph (from retirement to death)
|
| 525 |
+
const retirementGraph = document.getElementById('retirementValueGraph');
|
| 526 |
+
retirementGraph.innerHTML = '<div class="graph-line"></div>';
|
| 527 |
+
|
| 528 |
+
// Add points to show the value decay
|
| 529 |
+
for (let y = retirementAge; y <= lifeExpectancy; y++) {
|
| 530 |
+
const yearsFromRetirement = y - retirementAge;
|
| 531 |
+
const valuePercent = calculateRetirementValueMultiplier(yearsFromRetirement, retirementMultiplier);
|
| 532 |
+
const point = document.createElement('div');
|
| 533 |
+
point.style.position = 'absolute';
|
| 534 |
+
point.style.bottom = '0';
|
| 535 |
+
point.style.left = `${((y - retirementAge) / (lifeExpectancy - retirementAge)) * 100}%`;
|
| 536 |
+
point.style.width = '2px';
|
| 537 |
+
point.style.height = `${Math.min(100, valuePercent)}%`;
|
| 538 |
+
point.style.backgroundColor = '#3b82f6';
|
| 539 |
+
retirementGraph.appendChild(point);
|
| 540 |
+
}
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
// Update the tax bracket graph
|
| 544 |
+
function updateTaxBracketGraph() {
|
| 545 |
+
const salary = parseInt(document.getElementById('salary').value) * 12; // Annual salary
|
| 546 |
+
const graph = document.getElementById('taxBracketGraph');
|
| 547 |
+
graph.innerHTML = '<div class="graph-line"></div>';
|
| 548 |
+
|
| 549 |
+
// Find the maximum bracket that applies to our salary
|
| 550 |
+
let maxBracket = 0;
|
| 551 |
+
for (const bracket of TAX_BRACKETS) {
|
| 552 |
+
if (salary > bracket.min) {
|
| 553 |
+
maxBracket = Math.max(maxBracket, bracket.max);
|
| 554 |
+
}
|
| 555 |
+
}
|
| 556 |
+
|
| 557 |
+
// Add tax brackets to the graph
|
| 558 |
+
for (const bracket of TAX_BRACKETS) {
|
| 559 |
+
if (bracket.max > maxBracket * 1.5) continue; // Don't show brackets far beyond our salary
|
| 560 |
+
|
| 561 |
+
const bracketDiv = document.createElement('div');
|
| 562 |
+
bracketDiv.className = 'tax-bracket';
|
| 563 |
+
bracketDiv.style.left = `${(bracket.min / (maxBracket * 1.5)) * 100}%`;
|
| 564 |
+
bracketDiv.style.width = `${((bracket.max - bracket.min) / (maxBracket * 1.5)) * 100}%`;
|
| 565 |
+
graph.appendChild(bracketDiv);
|
| 566 |
+
|
| 567 |
+
const label = document.createElement('div');
|
| 568 |
+
label.className = 'tax-bracket-label';
|
| 569 |
+
label.textContent = `${bracket.name} (${bracket.rate * 100}%)`;
|
| 570 |
+
label.style.left = `${(bracket.min / (maxBracket * 1.5)) * 100}%`;
|
| 571 |
+
graph.appendChild(label);
|
| 572 |
+
}
|
| 573 |
+
|
| 574 |
+
// Add current salary marker
|
| 575 |
+
const marker = document.createElement('div');
|
| 576 |
+
marker.style.position = 'absolute';
|
| 577 |
+
marker.style.bottom = '0';
|
| 578 |
+
marker.style.left = `${(salary / (maxBracket * 1.5)) * 100}%`;
|
| 579 |
+
marker.style.width = '2px';
|
| 580 |
+
marker.style.height = '100%';
|
| 581 |
+
marker.style.backgroundColor = '#ef4444';
|
| 582 |
+
graph.appendChild(marker);
|
| 583 |
+
|
| 584 |
+
const markerLabel = document.createElement('div');
|
| 585 |
+
markerLabel.className = 'tax-bracket-label';
|
| 586 |
+
markerLabel.textContent = `Din lön: ${formatNumber(salary)} kr`;
|
| 587 |
+
markerLabel.style.left = `${(salary / (maxBracket * 1.5)) * 100}%`;
|
| 588 |
+
markerLabel.style.color = '#ef4444';
|
| 589 |
+
graph.appendChild(markerLabel);
|
| 590 |
+
|
| 591 |
+
// Update marginal tax text
|
| 592 |
+
const marginalTax = calculateMarginalTax(salary) * 100;
|
| 593 |
+
document.getElementById('marginalTaxText').innerHTML = `
|
| 594 |
+
Din nuvarande årslön: <strong>${formatNumber(salary)} kr</strong><br>
|
| 595 |
+
Marginaltaxesats: <strong>${marginalTax}%</strong><br>
|
| 596 |
+
Löneväxling kan spara <strong>${marginalTax + 31.42}%</strong> (skatt + arbetsgivaravgifter)
|
| 597 |
+
`;
|
| 598 |
}
|
| 599 |
|
| 600 |
// Initialize charts
|
| 601 |
+
let lifecycleChart;
|
| 602 |
+
let pensionChart;
|
| 603 |
|
| 604 |
function initCharts() {
|
| 605 |
+
const lifecycleCtx = document.getElementById('lifecycleChart').getContext('2d');
|
| 606 |
+
lifecycleChart = new Chart(lifecycleCtx, {
|
| 607 |
+
type: 'line',
|
| 608 |
data: {
|
| 609 |
+
labels: [],
|
| 610 |
+
datasets: [
|
| 611 |
+
{
|
| 612 |
+
label: 'Upplevt värde över tid',
|
| 613 |
+
data: [],
|
| 614 |
+
borderColor: '#8b5cf6',
|
| 615 |
+
backgroundColor: 'rgba(139, 92, 246, 0.1)',
|
| 616 |
+
borderWidth: 3,
|
| 617 |
+
fill: true,
|
| 618 |
+
tension: 0.3
|
| 619 |
+
}
|
| 620 |
+
]
|
| 621 |
},
|
| 622 |
options: {
|
| 623 |
responsive: true,
|
| 624 |
maintainAspectRatio: false,
|
| 625 |
plugins: {
|
|
|
|
|
|
|
|
|
|
| 626 |
tooltip: {
|
| 627 |
callbacks: {
|
| 628 |
label: function(context) {
|
| 629 |
+
return context.dataset.label + ': ' + context.raw.toFixed(2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 630 |
}
|
| 631 |
}
|
| 632 |
}
|
| 633 |
+
},
|
| 634 |
+
scales: {
|
| 635 |
+
y: {
|
| 636 |
+
title: {
|
| 637 |
+
display: true,
|
| 638 |
+
text: 'Relativt värde'
|
| 639 |
+
}
|
| 640 |
+
},
|
| 641 |
+
x: {
|
| 642 |
+
title: {
|
| 643 |
+
display: true,
|
| 644 |
+
text: 'Ålder'
|
| 645 |
+
}
|
| 646 |
+
}
|
| 647 |
}
|
| 648 |
}
|
| 649 |
});
|
| 650 |
|
| 651 |
+
const pensionCtx = document.getElementById('pensionChart').getContext('2d');
|
| 652 |
+
pensionChart = new Chart(pensionCtx, {
|
| 653 |
+
type: 'bar',
|
| 654 |
data: {
|
| 655 |
+
labels: ['Arbetsgivaravgifter sparade', 'Skatt sparad', 'Pensionsavsättning'],
|
| 656 |
+
datasets: [{
|
| 657 |
+
data: [0, 0, 0],
|
| 658 |
+
backgroundColor: [
|
| 659 |
+
'#3b82f6',
|
| 660 |
+
'#10b981',
|
| 661 |
+
'#f59e0b'
|
| 662 |
+
],
|
| 663 |
+
borderWidth: 1
|
| 664 |
+
}]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
},
|
| 666 |
options: {
|
| 667 |
responsive: true,
|
| 668 |
maintainAspectRatio: false,
|
| 669 |
plugins: {
|
| 670 |
+
legend: {
|
| 671 |
+
display: false
|
| 672 |
+
},
|
| 673 |
tooltip: {
|
| 674 |
callbacks: {
|
| 675 |
label: function(context) {
|
| 676 |
+
return context.label + ': ' + formatNumber(context.raw) + ' kr';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 677 |
}
|
| 678 |
}
|
| 679 |
}
|
|
|
|
| 692 |
});
|
| 693 |
}
|
| 694 |
|
| 695 |
+
// Calculate the optimal salary exchange percentage
|
| 696 |
+
function calculateOptimalStrategy() {
|
| 697 |
// Get input values
|
| 698 |
const salary = parseInt(document.getElementById('salary').value);
|
| 699 |
+
const salaryGrowth = parseFloat(document.getElementById('salaryGrowth').value);
|
| 700 |
const age = parseInt(document.getElementById('age').value);
|
| 701 |
const retirementAge = parseInt(document.getElementById('retirementAge').value);
|
| 702 |
+
const lifeExpectancy = parseInt(document.getElementById('lifeExpectancy').value);
|
| 703 |
+
const currentMultiplier = parseInt(document.getElementById('currentValueMultiplier').value) / 100;
|
| 704 |
+
const retirementMultiplier = parseInt(document.getElementById('retirementValueMultiplier').value) / 100;
|
| 705 |
+
const endOfLifeReserve = parseInt(document.getElementById('endOfLifeReserve').value) / 100;
|
| 706 |
+
const pensionContribution = parseFloat(document.getElementById('pensionContribution').value) / 100;
|
| 707 |
const expectedReturn = parseFloat(document.getElementById('expectedReturn').value);
|
| 708 |
|
| 709 |
+
// Calculate years to retirement and retirement duration
|
| 710 |
const yearsToRetirement = retirementAge - age;
|
| 711 |
+
const retirementDuration = lifeExpectancy - retirementAge;
|
| 712 |
|
| 713 |
+
// Test different salary exchange percentages to find the optimal one
|
| 714 |
+
let maxValue = -Infinity;
|
| 715 |
+
let optimalExchange = 0;
|
| 716 |
+
let optimalPension = 0;
|
| 717 |
+
let optimalValues = [];
|
|
|
|
|
|
|
|
|
|
| 718 |
|
| 719 |
+
for (let exchange = 0; exchange <= 100; exchange += 5) {
|
| 720 |
+
// Calculate pension growth with this exchange percentage
|
| 721 |
+
const totalPensionContribution = salary * pensionContribution;
|
| 722 |
+
const exchangedAmount = totalPensionContribution * (exchange / 100);
|
| 723 |
+
const regularPensionContribution = totalPensionContribution - exchangedAmount;
|
| 724 |
+
|
| 725 |
+
// Calculate tax savings from salary exchange
|
| 726 |
+
const taxRate = calculateTotalTax(salary) / salary;
|
| 727 |
+
const marginalTaxRate = calculateMarginalTax(salary * 12) / 100;
|
| 728 |
+
const employerContributionRate = 0.3142;
|
| 729 |
+
|
| 730 |
+
const taxSaved = exchangedAmount * marginalTaxRate;
|
| 731 |
+
const employerContributionsSaved = exchangedAmount * employerContributionRate;
|
| 732 |
+
const totalSavings = taxSaved + employerContributionsSaved;
|
| 733 |
+
|
| 734 |
+
// Calculate net salary
|
| 735 |
+
const taxWithoutExchange = calculateTotalTax(salary);
|
| 736 |
+
const taxWithExchange = calculateTotalTax(salary - exchangedAmount);
|
| 737 |
+
const netSalaryWithoutExchange = salary - taxWithoutExchange;
|
| 738 |
+
const netSalaryWithExchange = (salary - exchangedAmount) - taxWithExchange;
|
| 739 |
+
|
| 740 |
+
// Calculate pension growth with salary growth
|
| 741 |
+
const monthlyContributionWithExchange = (regularPensionContribution + exchangedAmount + totalSavings) / 12;
|
| 742 |
+
const monthlyContributionWithoutExchange = (regularPensionContribution + (exchangedAmount * (1 - marginalTaxRate))) / 12;
|
| 743 |
+
|
| 744 |
+
const pensionWithExchange = calculateCompoundInterest(0, expectedReturn, yearsToRetirement, monthlyContributionWithExchange, salaryGrowth);
|
| 745 |
+
const pensionWithoutExchange = calculateCompoundInterest(0, expectedReturn, yearsToRetirement, monthlyContributionWithoutExchange, salaryGrowth);
|
| 746 |
+
|
| 747 |
+
// Calculate perceived value over lifecycle
|
| 748 |
+
let totalValue = 0;
|
| 749 |
+
const yearlyValues = [];
|
| 750 |
+
|
| 751 |
+
// Working years (current to retirement)
|
| 752 |
+
for (let year = 0; year < yearsToRetirement; year++) {
|
| 753 |
+
const currentAge = age + year;
|
| 754 |
+
const yearsRemaining = yearsToRetirement - year;
|
| 755 |
+
|
| 756 |
+
// Calculate salary at this year
|
| 757 |
+
const yearSalary = calculateProjectedSalary(salary, salaryGrowth, year);
|
| 758 |
+
const yearExchangedAmount = yearSalary * pensionContribution * (exchange / 100);
|
| 759 |
+
const yearTaxWithExchange = calculateTotalTax(yearSalary - yearExchangedAmount);
|
| 760 |
+
const yearNetSalary = (yearSalary - yearExchangedAmount) - yearTaxWithExchange;
|
| 761 |
+
|
| 762 |
+
// Calculate value multiplier for this year
|
| 763 |
+
const valueMultiplier = 1 + (currentMultiplier - 1) * (yearsRemaining / yearsToRetirement);
|
| 764 |
+
|
| 765 |
+
// Add to total value
|
| 766 |
+
const yearValue = yearNetSalary * valueMultiplier;
|
| 767 |
+
totalValue += yearValue;
|
| 768 |
+
yearlyValues.push({
|
| 769 |
+
age: currentAge,
|
| 770 |
+
value: yearValue,
|
| 771 |
+
type: 'working'
|
| 772 |
+
});
|
| 773 |
+
}
|
| 774 |
+
|
| 775 |
+
// Retirement years (retirement to death)
|
| 776 |
+
const annualPensionWithdrawal = (pensionWithExchange * (1 - endOfLifeReserve)) / retirementDuration;
|
| 777 |
+
let remainingPension = pensionWithExchange;
|
| 778 |
+
|
| 779 |
+
for (let year = 0; year < retirementDuration; year++) {
|
| 780 |
+
const currentAge = retirementAge + year;
|
| 781 |
+
|
| 782 |
+
// Calculate pension withdrawal for this year
|
| 783 |
+
const withdrawal = Math.min(annualPensionWithdrawal, remainingPension);
|
| 784 |
+
remainingPension -= withdrawal;
|
| 785 |
+
|
| 786 |
+
// Calculate value multiplier for this year
|
| 787 |
+
const valueMultiplier = 1 + (retirementMultiplier - 1) * (year / retirementDuration);
|
| 788 |
+
|
| 789 |
+
// Add to total value
|
| 790 |
+
const yearValue = withdrawal * valueMultiplier;
|
| 791 |
+
totalValue += yearValue;
|
| 792 |
+
yearlyValues.push({
|
| 793 |
+
age: currentAge,
|
| 794 |
+
value: yearValue,
|
| 795 |
+
type: 'retirement'
|
| 796 |
+
});
|
| 797 |
+
}
|
| 798 |
+
|
| 799 |
+
// Add remaining pension at death (if any)
|
| 800 |
+
if (remainingPension > 0) {
|
| 801 |
+
totalValue += remainingPension * retirementMultiplier;
|
| 802 |
+
yearlyValues.push({
|
| 803 |
+
age: lifeExpectancy,
|
| 804 |
+
value: remainingPension * retirementMultiplier,
|
| 805 |
+
type: 'reserve'
|
| 806 |
+
});
|
| 807 |
+
}
|
| 808 |
+
|
| 809 |
+
// Check if this is the best strategy so far
|
| 810 |
+
if (totalValue > maxValue) {
|
| 811 |
+
maxValue = totalValue;
|
| 812 |
+
optimalExchange = exchange;
|
| 813 |
+
optimalPension = pensionWithExchange;
|
| 814 |
+
optimalValues = yearlyValues;
|
| 815 |
+
}
|
| 816 |
+
}
|
| 817 |
|
| 818 |
+
// Update results display
|
| 819 |
+
document.getElementById('optimalExchange').textContent = optimalExchange + '%';
|
| 820 |
+
document.getElementById('maxValue').textContent = formatNumber(Math.round(maxValue / 1000)) + 'k';
|
| 821 |
+
document.getElementById('totalPension').textContent = formatNumber(Math.round(optimalPension)) + ' kr';
|
| 822 |
|
| 823 |
+
// Update lifecycle chart
|
| 824 |
+
const ages = optimalValues.map(v => v.age);
|
| 825 |
+
const values = optimalValues.map(v => v.value / 1000); // Scale down for chart
|
| 826 |
+
lifecycleChart.data.labels = ages;
|
| 827 |
+
lifecycleChart.data.datasets[0].data = values;
|
| 828 |
+
lifecycleChart.update();
|
| 829 |
|
| 830 |
+
// Update pension chart with optimal exchange details
|
| 831 |
+
const salaryInput = parseInt(document.getElementById('salary').value);
|
| 832 |
+
const pensionContributionInput = parseFloat(document.getElementById('pensionContribution').value) / 100;
|
| 833 |
+
const exchangedAmount = salaryInput * pensionContributionInput * (optimalExchange / 100);
|
| 834 |
+
const taxSaved = exchangedAmount * calculateMarginalTax(salaryInput * 12) / 100;
|
| 835 |
+
const employerContributionsSaved = exchangedAmount * 0.3142;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 836 |
|
| 837 |
+
pensionChart.data.datasets[0].data = [
|
| 838 |
+
Math.round(employerContributionsSaved * 12),
|
| 839 |
+
Math.round(taxSaved * 12),
|
| 840 |
+
Math.round((salaryInput * pensionContributionInput - exchangedAmount + exchangedAmount + taxSaved + employerContributionsSaved) * 12)
|
| 841 |
+
];
|
| 842 |
+
pensionChart.update();
|
| 843 |
|
| 844 |
+
// Update strategy tips
|
| 845 |
+
let tips = '';
|
| 846 |
+
if (optimalExchange === 0) {
|
| 847 |
+
tips = 'Baserat på dina parametrar är ingen löneväxling optimal. Detta kan bero på:';
|
| 848 |
+
tips += '<ul class="list-disc pl-5 mt-1">';
|
| 849 |
+
tips += '<li>Låg marginaltaxesats</li>';
|
| 850 |
+
tips += '<li>Hög värdering av nuvarande inkomst</li>';
|
| 851 |
+
tips += '<li>Lång tid till pension</li>';
|
| 852 |
+
tips += '</ul>';
|
| 853 |
+
} else if (optimalExchange < 50) {
|
| 854 |
+
tips = 'En del löneväxling (' + optimalExchange + '%) är optimal. Detta balanserar:';
|
| 855 |
+
tips += '<ul class="list-disc pl-5 mt-1">';
|
| 856 |
+
tips += '<li>Skattesparande nu</li>';
|
| 857 |
+
tips += '<li>Framtida pensionsinkomst</li>';
|
| 858 |
+
tips += '<li>Din subjektiva värdering av pengar över tid</li>';
|
| 859 |
+
tips += '</ul>';
|
| 860 |
+
} else if (optimalExchange < 100) {
|
| 861 |
+
tips = 'Hög löneväxling (' + optimalExchange + '%) är optimal. Detta tyder på:';
|
| 862 |
+
tips += '<ul class="list-disc pl-5 mt-1">';
|
| 863 |
+
tips += '<li>Hög marginaltaxesats</li>';
|
| 864 |
+
tips += '<li>Relativt låg värdering av nuvarande inkomst</li>';
|
| 865 |
+
tips += '<li>Fördelaktig pensionsavkastning</li>';
|
| 866 |
+
tips += '</ul>';
|
| 867 |
} else {
|
| 868 |
+
tips = 'Full löneväxling (100%) är optimal. Detta är typiskt när:';
|
| 869 |
+
tips += '<ul class="list-disc pl-5 mt-1">';
|
| 870 |
+
tips += '<li>Mycket hög marginaltaxesats</li>';
|
| 871 |
+
tips += '<li>Låg värdering av nuvarande inkomst</li>';
|
| 872 |
+
tips += '<li>Kort tid till pension</li>';
|
| 873 |
+
tips += '</ul>';
|
| 874 |
}
|
| 875 |
|
| 876 |
+
// Add specific recommendations based on parameters
|
| 877 |
+
if (yearsToRetirement < 10) {
|
| 878 |
+
tips += '<p class="mt-2">Med mindre än 10 år till pension kan löneväxling vara särskilt fördelaktigt.</p>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 879 |
}
|
| 880 |
|
| 881 |
+
if (calculateMarginalTax(salary * 12) > 0.5) {
|
| 882 |
+
tips += '<p class="mt-2">Din höga marginaltaxesats gör löneväxling extra attraktiv.</p>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 883 |
}
|
| 884 |
|
| 885 |
+
if (currentMultiplier < 2) {
|
| 886 |
+
tips += '<p class="mt-2">Din relativt låga värdering av nuvarande inkomst gör pensionssparande mer attraktivt.</p>';
|
|
|
|
|
|
|
| 887 |
}
|
| 888 |
|
| 889 |
+
document.getElementById('strategyTips').innerHTML = tips;
|
| 890 |
|
| 891 |
// Add animation classes
|
| 892 |
+
document.getElementById('optimalExchange').classList.add('animate-grow');
|
| 893 |
+
document.getElementById('maxValue').classList.add('animate-grow');
|
| 894 |
document.getElementById('totalPension').classList.add('animate-grow');
|
|
|
|
| 895 |
|
| 896 |
// Remove animation classes after animation completes
|
| 897 |
setTimeout(() => {
|
| 898 |
+
document.getElementById('optimalExchange').classList.remove('animate-grow');
|
| 899 |
+
document.getElementById('maxValue').classList.remove('animate-grow');
|
| 900 |
document.getElementById('totalPension').classList.remove('animate-grow');
|
| 901 |
}, 1500);
|
| 902 |
}
|
|
|
|
| 912 |
|
| 913 |
// Set up calculate button
|
| 914 |
document.getElementById('calculateBtn').addEventListener('click', function() {
|
| 915 |
+
calculateOptimalStrategy();
|
| 916 |
});
|
| 917 |
|
| 918 |
// Initialize display values and charts
|
|
|
|
| 920 |
initCharts();
|
| 921 |
|
| 922 |
// Calculate initial results
|
| 923 |
+
calculateOptimalStrategy();
|
| 924 |
});
|
| 925 |
</script>
|
| 926 |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Jausing/misc-stuff" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
|
prompts.txt
CHANGED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Addera förväntad löneutveckling, tack
|
| 2 |
+
Alltså det är ett lite komplicerat optimeringsproblem... Hårda parametrar är följande Nuvarande lön Nuvarande ålder Semi-flexibla parametrar är följande (parametrar du har en viss kontroll över, men kanske inte till 100%) Förväntad löneutveckling per år Pensionsålder Förväntad dödsålder Subjektivt avtagande funktion som sätter värdet på pengar idag kontra efter pensionsålder. Ponera exempelvis att du värderar samma inflationsjusterade summa pengar till 300% av hur du värderar pengarna efter pensionsålder idag, men dagen innan pensionen ska ju motsvarande tal vara typ 100.001% Subjektivt avtagande funktion som sätter värdet på pengar från pensionsålder till förväntad dödsålder. Notera att du troligtvis inte har värdet 0 vid din förväntade dödsålder, eftersom du vill ha lite pengar kvar om du skulle leva längre, men du kan ju också ha en "die with zero, aktiv dödshjälp om pengarna tar slut"-strategi Vi vill ju maximera perceived value för pengarna över tid. Målfunktionen måste också ha med de olika skatte-skikten osv för att det ska bli rätt
|