Geopolitical Tensions & Market Risk Simulator
Define Scenario
Primary Affected Region(s): (Select all applicable)
Asset Classes to Analyze: (Select all applicable)
Simulation Results
Event Type: ${scenario.eventType}
Severity: ${scenario.severity}
Affected Region(s): ${scenario.regions}
Time Horizon: ${scenario.timeHorizon}
Baseline Sentiment: ${scenario.marketSentiment}
`; } /** * Core simulation logic. This is highly simplified and heuristic. * A real model would be vastly more complex. */ function getSimulatedImpact(scenario, asset) { let impact = { assetName: asset.label, assetValue: asset.value, direction: 'Neutral ~', directionClass: 'impact-direction-neutral', magnitude: 'Low', riskFactors: ['General market uncertainty.'], safeHavens: ['Diversification is key.'], rationale: 'Impact depends on specific event characteristics not fully modeled.' }; let severityMultiplier = 1; // low:1, medium:1.5, high:2, crisis:2.5 if (scenario.severityValue === 'medium') severityMultiplier = 1.5; else if (scenario.severityValue === 'high') severityMultiplier = 2; else if (scenario.severityValue === 'crisis') severityMultiplier = 2.5; // Basic impact based on event type - highly generalized switch (scenario.eventTypeValue) { case 'trade_dispute': impact.riskFactors = ['Tariff imposition', 'Supply chain disruptions', 'Reduced international trade', 'Sector-specific impacts (e.g., export-oriented).']; if (['global_equities', 'regional_equities', 'industrial_metals', 'fx_emerging'].includes(asset.value)) { impact.direction = 'Downward Pressure ▼'; impact.directionClass = 'impact-direction-negative'; } else if (asset.value === 'gold') { impact.direction = 'Slight Upward Pressure ▲'; impact.directionClass = 'impact-direction-positive'; } break; case 'regional_conflict': impact.riskFactors = ['Localized economic disruption', 'Potential energy supply shocks (if relevant region)', 'Capital flight from affected region', 'Increased defense spending.']; impact.safeHavens = ['Gold', 'USD', 'Developed market government bonds.']; if (asset.value === 'oil' && (scenario.regionsValues.includes('mena') || scenario.regionsValues.includes('global'))) { impact.direction = 'Strong Upward Pressure ▲▲'; impact.directionClass = 'impact-direction-positive'; impact.magnitude = 'High'; } else if (asset.value === 'regional_equities' && scenario.regionsValues.length > 0) { impact.direction = 'Strong Downward Pressure ▼▼'; impact.directionClass = 'impact-direction-negative'; impact.magnitude = 'High'; } else if (['global_equities', 'corp_bonds'].includes(asset.value)) { impact.direction = 'Downward Pressure ▼'; impact.directionClass = 'impact-direction-negative'; } else if (asset.value === 'gold') { impact.direction = 'Upward Pressure ▲'; impact.directionClass = 'impact-direction-positive'; } break; case 'political_instability': impact.riskFactors = ['Policy uncertainty', 'Reduced FDI', 'Currency devaluation (affected region)', 'Social unrest impact on production.']; impact.safeHavens = ['Hard currencies (USD, CHF)', 'Gold.']; if (asset.value === 'regional_equities' || asset.value === 'fx_emerging' && scenario.regionsValues.some(r => ['emerging_markets', 'latin_america', 'sub_saharan_africa', 'mena'].includes(r))) { impact.direction = 'Strong Downward Pressure ▼▼'; impact.directionClass = 'impact-direction-negative'; impact.magnitude = 'High'; } else if (asset.value === 'global_equities') { impact.direction = 'Downward Pressure ▼'; impact.directionClass = 'impact-direction-negative'; } break; case 'terror_cyber': impact.riskFactors = ['Disruption to critical infrastructure', 'Loss of consumer/business confidence', 'Increased security spending', 'Sector impacts (e.g., travel, tech).']; impact.safeHavens = ['Government bonds (developed)', 'Cash.']; if (['global_equities', 'regional_equities'].includes(asset.value)) { impact.direction = 'Downward Pressure ▼'; impact.directionClass = 'impact-direction-negative'; impact.magnitude = 'Moderate'; } if (scenario.severityValue === 'crisis' && asset.value.includes('equities')) impact.magnitude = 'High'; break; case 'sanctions': impact.riskFactors = ['Trade restrictions with sanctioned entity', 'Financial transaction difficulties', 'Impact on companies exposed to sanctioned entity', 'Retaliatory measures.']; if (asset.value.includes('equities') && scenario.regionsValues.includes('global')) { // Assuming broad sanctions impact.direction = 'Volatile/Mixed ↕'; impact.directionClass = 'impact-direction-volatile'; } else if (asset.value === 'fx_emerging' && scenario.regionsValues.some(r => ['emerging_markets'].includes(r))) { // Assume sanctions on an EM impact.direction = 'Strong Downward Pressure ▼▼'; impact.directionClass = 'impact-direction-negative'; impact.magnitude = 'High'; } break; } // Adjust magnitude based on severityMultiplier, capped at a descriptive level let numericMagnitude = 1; // low if (impact.magnitude === 'Moderate') numericMagnitude = 2; else if (impact.magnitude === 'High') numericMagnitude = 3; else if (impact.magnitude === 'Severe') numericMagnitude = 4; numericMagnitude *= severityMultiplier; if (scenario.marketSentimentValue === 'pessimistic' && impact.directionClass === 'impact-direction-negative') numericMagnitude *= 1.2; if (scenario.marketSentimentValue === 'optimistic' && impact.directionClass === 'impact-direction-positive') numericMagnitude *= 1.2; if (numericMagnitude <= 1.5) impact.magnitude = 'Low'; else if (numericMagnitude <= 2.5) impact.magnitude = 'Moderate'; else if (numericMagnitude <= 3.5) impact.magnitude = 'High'; else impact.magnitude = 'Severe'; // Default rationale based on direction and magnitude if (impact.direction !== 'Neutral ~') { impact.rationale = `The simulated ${scenario.eventType.toLowerCase()} with ${scenario.severity.toLowerCase()} severity is expected to exert ${impact.direction.toLowerCase()} on ${asset.label.toLowerCase()} with ${impact.magnitude.toLowerCase()} magnitude. Key drivers include: ${impact.riskFactors[0] || 'general market dynamics'}.`; if (scenario.timeHorizonValue === 'long') impact.rationale += ` Long-term effects could be more pronounced.`; else if (scenario.timeHorizonValue === 'short') impact.rationale += ` Short-term volatility is likely.`; } // Specific asset considerations (examples, extend as needed) if (asset.value === 'gold' && impact.directionClass !== 'impact-direction-positive' && severityMultiplier > 1.5) { impact.direction = 'Upward Pressure ▲'; impact.directionClass = 'impact-direction-positive'; impact.rationale = `Gold often acts as a safe haven during significant geopolitical uncertainty, leading to increased demand.`; } if (asset.value === 'oil' && scenario.eventTypeValue === 'regional_conflict' && scenario.regionsValues.some(r => ['mena', 'global'].includes(r))) { impact.rationale = `Regional conflicts in major oil-producing areas typically disrupt supply or create fears of disruption, leading to price spikes. Severity is ${scenario.severity.toLowerCase()}.`; } return impact; } function displayImpactCard(impact) { const card = document.createElement('div'); card.className = 'impact-card'; let riskFactorsHTML = impact.riskFactors.length > 0 ? `- ${impact.riskFactors.map(rf => `
- ${rf} `).join('')}
N/A
'; let safeHavensHTML = impact.safeHavens.length > 0 ? `- ${impact.safeHavens.map(sh => `
- ${sh} `).join('')}
N/A
'; card.innerHTML = `${impact.assetName}
Anticipated Impact: ${impact.direction}
Expected Magnitude: ${impact.magnitude}
Rationale: ${impact.rationale}
Key Risk Factors:
${riskFactorsHTML}Potential Safe Havens/Considerations:
${safeHavensHTML} `; resultsDisplayAreaEl.appendChild(card); } function displayMessage(text, type) { messageArea.textContent = text; messageArea.className = type === 'error' ? 'message-error' : (type === 'info' ? 'message-info' : ''); } // PDF Download Functionality downloadPdfButton.addEventListener('click', function () { const pdfExportContent = document.getElementById('pdfExportContent'); if (!pdfExportContent) { displayMessage('Error: PDF content area not found.', 'error'); return; } // Ensure section is visible for capture const originalDisplay = pdfExportContent.style.display; pdfExportContent.style.display = 'block'; html2canvas(pdfExportContent, { scale: 2, useCORS: true, logging: false }).then(canvas => { const imgData = canvas.toDataURL('image/png'); const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const canvasWidth = canvas.width; const canvasHeight = canvas.height; // Calculate aspect ratio const contentAspectRatio = canvasWidth / canvasHeight; const margin = 15; // 15mm margin on all sides const usableWidth = pdfWidth - (2 * margin); const usableHeight = pdfHeight - (2 * margin); let imgWidth = usableWidth; let imgHeight = imgWidth / contentAspectRatio; // If calculated height is too much, scale by height if (imgHeight > usableHeight) { imgHeight = usableHeight; imgWidth = imgHeight * contentAspectRatio; } // If after scaling by height, width is too much (shouldn't happen if logic is right, but good check) if (imgWidth > usableWidth) { imgWidth = usableWidth; imgHeight = imgWidth / contentAspectRatio; } const x = margin + (usableWidth - imgWidth) / 2; // Center within usable area const y = margin; // Add a title to the PDF pdf.setFontSize(18); pdf.text("Geopolitical Risk Simulation Report", pdfWidth / 2, margin / 1.5 , { align: "center" }); pdf.addImage(imgData, 'PNG', x, y, imgWidth, imgHeight); pdf.save('geopolitical-risk-simulation.pdf'); // Restore original display if it was none pdfExportContent.style.display = originalDisplay; }).catch(error => { console.error('Error generating PDF:', error); displayMessage('Failed to generate PDF. See console for details.', 'error'); pdfExportContent.style.display = originalDisplay; }); }); });