|
|
|
|
|
|
|
|
|
import math |
|
import numpy as np |
|
import pandas as pd |
|
import gradio as gr |
|
import plotly.graph_objects as go |
|
from plotly.subplots import make_subplots |
|
import json |
|
from datetime import datetime, timedelta |
|
import io |
|
import base64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
VISA_TYPES = { |
|
"UAE (Dubai)": { |
|
"Golden Visa": {"min_investment": 2000000, "duration": "10 years", "processing_time": "6 months"}, |
|
"Investor Visa": {"min_investment": 500000, "duration": "3 years", "processing_time": "3 months"}, |
|
"Freelancer Visa": {"min_investment": 0, "duration": "1 year", "processing_time": "1 month"} |
|
}, |
|
"UK": { |
|
"Innovator Founder": {"min_investment": 50000, "duration": "3 years", "processing_time": "6 months"}, |
|
"Global Talent": {"min_investment": 0, "duration": "5 years", "processing_time": "3 months"}, |
|
"Skilled Worker": {"min_investment": 0, "duration": "5 years", "processing_time": "3 months"} |
|
}, |
|
"USA (FL/TX/NV)": { |
|
"EB-5 Investor": {"min_investment": 800000, "duration": "2 years", "processing_time": "18 months"}, |
|
"L-1A Executive": {"min_investment": 0, "duration": "7 years", "processing_time": "4 months"}, |
|
"O-1 Extraordinary": {"min_investment": 0, "duration": "3 years", "processing_time": "3 months"} |
|
}, |
|
"Spain": { |
|
"Golden Visa": {"min_investment": 500000, "duration": "3 years", "processing_time": "3 months"}, |
|
"Entrepreneur Visa": {"min_investment": 50000, "duration": "2 years", "processing_time": "2 months"}, |
|
"Digital Nomad": {"min_investment": 0, "duration": "1 year", "processing_time": "1 month"} |
|
}, |
|
"Ireland": { |
|
"Investor Programme": {"min_investment": 1000000, "duration": "5 years", "processing_time": "6 months"}, |
|
"Start-up Entrepreneur": {"min_investment": 75000, "duration": "2 years", "processing_time": "4 months"}, |
|
"Critical Skills": {"min_investment": 0, "duration": "2 years", "processing_time": "3 months"} |
|
} |
|
} |
|
|
|
CURRENCIES = { |
|
"EUR": 1.0, |
|
"USD": 1.08, |
|
"GBP": 0.85, |
|
"AED": 3.67 |
|
} |
|
|
|
|
|
COUNTRY_CONFIG = { |
|
"UAE (Dubai)": { |
|
"corp_tax": 0.09, "pers_tax": 0.00, "rev_mult": 3.0, "margin_delta_pp": 5.0, |
|
"living_month": 9000.0, "ongoing_month": 1500.0, "setup_once": 35000.0, |
|
"currency": "AED", "inflation": 2.5, "market_growth": 8.5, "ease_business": 9.2, |
|
"tax_treaties": 95, "banking_score": 8.8, "legal_system": "Civil Law", |
|
"additional_costs": { |
|
"visa_renewal": 5000, "banking_setup": 3000, "legal_compliance": 8000, |
|
"audit_annual": 12000, "insurance": 6000 |
|
} |
|
}, |
|
"UK": { |
|
"corp_tax": 0.25, "pers_tax": 0.27, "rev_mult": 1.5, "margin_delta_pp": 2.0, |
|
"living_month": 6200.0, "ongoing_month": 1100.0, "setup_once": 18000.0, |
|
"currency": "GBP", "inflation": 4.2, "market_growth": 2.1, "ease_business": 8.1, |
|
"tax_treaties": 130, "banking_score": 9.1, "legal_system": "Common Law", |
|
"additional_costs": { |
|
"visa_renewal": 8000, "banking_setup": 2000, "legal_compliance": 15000, |
|
"audit_annual": 8000, "insurance": 4000 |
|
} |
|
}, |
|
"USA (FL/TX/NV)": { |
|
"corp_tax": 0.21, "pers_tax": 0.22, "rev_mult": 1.8, "margin_delta_pp": 4.0, |
|
"living_month": 7000.0, "ongoing_month": 1300.0, "setup_once": 20000.0, |
|
"currency": "USD", "inflation": 3.1, "market_growth": 5.2, "ease_business": 8.7, |
|
"tax_treaties": 65, "banking_score": 8.9, "legal_system": "Common Law", |
|
"additional_costs": { |
|
"visa_renewal": 12000, "banking_setup": 5000, "legal_compliance": 20000, |
|
"audit_annual": 10000, "insurance": 8000 |
|
} |
|
}, |
|
"Spain": { |
|
"corp_tax": 0.25, "pers_tax": 0.24, "rev_mult": 1.4, "margin_delta_pp": 2.0, |
|
"living_month": 5000.0, "ongoing_month": 1000.0, "setup_once": 20000.0, |
|
"currency": "EUR", "inflation": 3.8, "market_growth": 3.2, "ease_business": 7.3, |
|
"tax_treaties": 100, "banking_score": 7.8, "legal_system": "Civil Law", |
|
"additional_costs": { |
|
"visa_renewal": 4000, "banking_setup": 2500, "legal_compliance": 12000, |
|
"audit_annual": 7000, "insurance": 3500 |
|
} |
|
}, |
|
"Ireland": { |
|
"corp_tax": 0.125, "pers_tax": 0.22, "rev_mult": 1.6, "margin_delta_pp": 3.0, |
|
"living_month": 6500.0, "ongoing_month": 1200.0, "setup_once": 25000.0, |
|
"currency": "EUR", "inflation": 2.8, "market_growth": 4.1, "ease_business": 8.4, |
|
"tax_treaties": 75, "banking_score": 8.2, "legal_system": "Common Law", |
|
"additional_costs": { |
|
"visa_renewal": 6000, "banking_setup": 3500, "legal_compliance": 10000, |
|
"audit_annual": 9000, "insurance": 4500 |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
CSS = """ |
|
:root { |
|
--vt-primary: #2563EB; --vt-accent: #10B981; --vt-danger: #EF4444; |
|
--vt-warning: #F59E0B; --vt-ink: #0F172A; --vt-muted: #64748B; --radius: 16px; |
|
} |
|
|
|
.gradio-container { max-width: 1400px !important; margin: 0 auto; } |
|
|
|
.vt-header { |
|
background: linear-gradient(135deg, #0F172A 0%, #1E293B 100%); |
|
color: #E2E8F0; padding: 18px 24px; border-radius: 16px; |
|
display: flex; align-items: center; justify-content: space-between; |
|
margin-bottom: 20px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); |
|
} |
|
|
|
.vt-hero { |
|
border-radius: var(--radius); padding: 24px 28px; |
|
background: linear-gradient(135deg, rgba(37,99,235,0.12), rgba(16,185,129,0.12)); |
|
border: 2px solid rgba(37,99,235,0.2); margin-bottom: 20px; |
|
backdrop-filter: blur(10px); |
|
} |
|
|
|
.vt-kpi { |
|
border-radius: var(--radius); padding: 20px; background: #FFFFFF; |
|
border: 2px solid #E2E8F0; margin-bottom: 12px; |
|
box-shadow: 0 4px 6px rgba(0,0,0,0.05); transition: all 0.3s ease; |
|
} |
|
.vt-kpi:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(0,0,0,0.1); } |
|
|
|
.vt-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; margin: 20px 0; } |
|
|
|
.sensitivity-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin: 16px 0; } |
|
|
|
.risk-indicator { |
|
padding: 8px 16px; border-radius: 20px; font-weight: 600; font-size: 12px; |
|
display: inline-block; margin: 4px; |
|
} |
|
.risk-low { background: rgba(16,185,129,0.1); color: #10B981; } |
|
.risk-medium { background: rgba(245,158,11,0.1); color: #F59E0B; } |
|
.risk-high { background: rgba(239,68,68,0.1); color: #EF4444; } |
|
|
|
.visa-card { |
|
background: #F8FAFC; border: 1px solid #E2E8F0; border-radius: 12px; |
|
padding: 16px; margin: 8px 0; |
|
} |
|
|
|
.ai-recommendation { |
|
background: linear-gradient(135deg, rgba(99,102,241,0.1), rgba(168,85,247,0.1)); |
|
border: 2px solid rgba(99,102,241,0.2); border-radius: 16px; |
|
padding: 20px; margin: 16px 0; |
|
} |
|
|
|
.export-section { |
|
background: #F1F5F9; border-radius: 12px; padding: 16px; |
|
margin: 16px 0; border-left: 4px solid var(--vt-primary); |
|
} |
|
|
|
.timeline-item { |
|
display: flex; align-items: center; margin: 12px 0; |
|
padding: 12px; background: #FFFFFF; border-radius: 8px; |
|
border-left: 3px solid var(--vt-primary); |
|
} |
|
|
|
.comparison-enhanced { |
|
background: #FFFFFF; border-radius: 16px; padding: 20px; |
|
box-shadow: 0 4px 6px rgba(0,0,0,0.05); margin: 16px 0; |
|
} |
|
|
|
@media (max-width: 768px) { |
|
.vt-grid { grid-template-columns: 1fr; } |
|
.sensitivity-grid { grid-template-columns: 1fr; } |
|
} |
|
""" |
|
|
|
THEME = gr.themes.Soft( |
|
primary_hue="blue", |
|
neutral_hue="slate", |
|
font=gr.themes.GoogleFont("Inter") |
|
).set( |
|
body_background_fill="#F8FAFC", |
|
body_text_color="#0F172A", |
|
button_primary_background_fill="#2563EB", |
|
button_primary_background_fill_hover="#1D4ED8" |
|
) |
|
|
|
|
|
|
|
|
|
|
|
def generate_ai_recommendation(result, country, personal_profile): |
|
"""Генерация AI-рекомендаций на основе результатов""" |
|
risk_level = "LOW" if result["payback_years"] <= 1.5 else ("MEDIUM" if result["payback_years"] <= 3.0 else "HIGH") |
|
|
|
recommendations = [] |
|
|
|
if result["total_5yr_roi"] < 50: |
|
recommendations.append("⚠️ Consider optimizing your business model before relocation") |
|
if result["payback_years"] > 3: |
|
recommendations.append("💡 Look into phased migration to reduce upfront costs") |
|
if country in ["UAE (Dubai)", "Ireland"] and result["irr_annual_pct"] > 25: |
|
recommendations.append("🚀 Strong financial case - consider accelerated timeline") |
|
|
|
recommendations.append(f"📊 Risk Level: {risk_level}") |
|
|
|
return recommendations |
|
|
|
def calculate_sensitivity_analysis(base_params, result): |
|
"""Анализ чувствительности ключевых параметров""" |
|
sensitivities = {} |
|
|
|
|
|
test_params = ['rev_mult', 'margin_delta_pp', 'success_pct'] |
|
|
|
for param in test_params: |
|
high_val = base_params[param] * 1.2 |
|
low_val = base_params[param] * 0.8 |
|
|
|
|
|
if param == 'rev_mult': |
|
impact_high = (high_val - base_params[param]) / base_params[param] * result["total_5yr_roi"] * 0.8 |
|
impact_low = (low_val - base_params[param]) / base_params[param] * result["total_5yr_roi"] * 0.8 |
|
else: |
|
impact_high = (high_val - base_params[param]) / base_params[param] * result["total_5yr_roi"] * 0.3 |
|
impact_low = (low_val - base_params[param]) / base_params[param] * result["total_5yr_roi"] * 0.3 |
|
|
|
sensitivities[param] = {"high": impact_high, "low": impact_low} |
|
|
|
return sensitivities |
|
|
|
def create_monte_carlo_simulation(base_params, iterations=1000): |
|
"""Упрощенная Монте-Карло симуляция""" |
|
results = [] |
|
np.random.seed(42) |
|
|
|
for _ in range(iterations): |
|
|
|
sim_rev_mult = base_params['rev_mult'] * np.random.normal(1.0, 0.15) |
|
sim_success = base_params['success_pct'] * np.random.normal(1.0, 0.10) |
|
sim_margin = base_params['margin_delta_pp'] * np.random.normal(1.0, 0.20) |
|
|
|
|
|
sim_roi = base_params.get('base_roi', 100) * (sim_rev_mult / base_params['rev_mult']) * (sim_success / base_params['success_pct']) |
|
results.append(max(0, sim_roi)) |
|
|
|
return { |
|
"mean": np.mean(results), |
|
"std": np.std(results), |
|
"percentile_10": np.percentile(results, 10), |
|
"percentile_90": np.percentile(results, 90), |
|
"probability_positive": len([r for r in results if r > 0]) / len(results) * 100 |
|
} |
|
|
|
def export_to_pdf_data(result, country, params): |
|
"""Подготовка данных для экспорта в PDF""" |
|
export_data = { |
|
"timestamp": datetime.now().isoformat(), |
|
"country": country, |
|
"parameters": params, |
|
"results": { |
|
"npv": result["npv"], |
|
"roi_5y": result["total_5yr_roi"], |
|
"payback_years": result["payback_years"], |
|
"irr_annual": result["irr_annual_pct"] |
|
} |
|
} |
|
|
|
|
|
json_str = json.dumps(export_data, indent=2, ensure_ascii=False) |
|
return json_str |
|
|
|
def create_enhanced_timeline(country): |
|
"""Создание расширенного таймлайна миграции""" |
|
if country not in VISA_TYPES: |
|
return "Timeline data not available for this country." |
|
|
|
visas = VISA_TYPES[country] |
|
timeline_html = "<div style='margin: 16px 0;'>" |
|
|
|
for visa_name, details in visas.items(): |
|
timeline_html += f""" |
|
<div class="timeline-item"> |
|
<div style="flex: 1;"> |
|
<h4 style="margin: 0 0 8px 0; color: var(--vt-primary);">{visa_name}</h4> |
|
<div style="font-size: 14px; color: var(--vt-muted);"> |
|
Investment: €{details['min_investment']:,} • |
|
Duration: {details['duration']} • |
|
Processing: {details['processing_time']} |
|
</div> |
|
</div> |
|
</div> |
|
""" |
|
|
|
timeline_html += "</div>" |
|
return timeline_html |
|
|
|
|
|
|
|
|
|
|
|
def clamp(x, lo, hi): |
|
return max(lo, min(hi, x)) |
|
|
|
def monthly_rate(annual_rate): |
|
return (1.0 + annual_rate) ** (1.0 / 12.0) - 1.0 |
|
|
|
def irr_bisection(cash, lo=-0.99, hi=5.0, iters=100, tol=1e-7): |
|
def npv(rate): return sum(cf / ((1 + rate) ** t) for t, cf in enumerate(cash)) |
|
f_lo, f_hi = npv(lo), npv(hi) |
|
if f_lo * f_hi > 0: return None |
|
for _ in range(iters): |
|
mid = (lo + hi) / 2 |
|
v = npv(mid) |
|
if abs(v) < tol: return mid |
|
if v > 0: lo = mid |
|
else: hi = mid |
|
return (lo + hi) / 2 |
|
|
|
def ramp_factor(m): |
|
return 0.6 if m <= 6 else (0.8 if m <= 12 else 1.0) |
|
|
|
def compute_enhanced_monthly_delta_cashflow( |
|
rev0, margin0_pct, corp0_pct, pers0_pct, living0, ongoing0, |
|
dest, rev_mult, margin_delta_pp, corp1_pct, pers1_pct, living1, ongoing1, |
|
capex_once, horizon_m, discount_annual_pct, success_pct, |
|
include_inflation=True, include_additional_costs=True |
|
): |
|
"""Расширенная версия вычисления с учетом инфляции и дополнительных расходов""" |
|
|
|
m0 = clamp(margin0_pct / 100.0, 0.0, 0.9) |
|
ct0 = clamp(corp0_pct / 100.0, 0.0, 0.6) |
|
pt0 = clamp(pers0_pct / 100.0, 0.0, 0.6) |
|
|
|
mult = max(0.0, rev_mult) |
|
mdelta = margin_delta_pp / 100.0 |
|
ct1 = clamp(corp1_pct / 100.0, 0.0, 0.6) |
|
pt1 = clamp(pers1_pct / 100.0, 0.0, 0.6) |
|
|
|
p = clamp(success_pct / 100.0, 0.01, 1.0) |
|
mr = monthly_rate(discount_annual_pct / 100.0) |
|
|
|
|
|
country_data = COUNTRY_CONFIG.get(dest, {}) |
|
inflation_rate = country_data.get('inflation', 3.0) / 100.0 / 12.0 |
|
additional_costs = country_data.get('additional_costs', {}) |
|
|
|
base_profit0 = rev0 * m0 |
|
after_tax0 = base_profit0 * (1 - ct0) * (1 - pt0) - living0 - ongoing0 |
|
|
|
rev1 = rev0 * mult |
|
m1 = clamp(m0 + mdelta, 0.01, 0.9) |
|
base_profit1 = rev1 * m1 |
|
after_tax1 = base_profit1 * (1 - ct1) * (1 - pt1) - living1 - ongoing1 |
|
|
|
delta_monthly = after_tax1 - after_tax0 |
|
|
|
cash = [-capex_once] |
|
cum = -capex_once |
|
months = [0] |
|
cum_series = [cum] |
|
payback_m = math.inf |
|
|
|
for m in range(1, horizon_m + 1): |
|
cf = delta_monthly * ramp_factor(m) * p |
|
|
|
|
|
if include_inflation: |
|
inflation_factor = (1 + inflation_rate) ** m |
|
cf = cf / inflation_factor |
|
|
|
|
|
if include_additional_costs and m % 12 == 0: |
|
cf -= additional_costs.get('audit_annual', 0) / 12 |
|
cf -= additional_costs.get('insurance', 0) / 12 |
|
|
|
if m % 36 == 0: |
|
cf -= additional_costs.get('visa_renewal', 0) |
|
|
|
cash.append(cf) |
|
cum += cf |
|
months.append(m) |
|
cum_series.append(cum) |
|
if math.isinf(payback_m) and cum >= 0: |
|
payback_m = m |
|
|
|
npv = sum(cf / ((1 + mr) ** t) for t, cf in enumerate(cash)) |
|
roi5y = (npv / capex_once * 100.0) if capex_once > 0 else (0.0 if npv <= 0 else math.inf) |
|
irr_m = irr_bisection(cash) |
|
irr_annual = ((1 + irr_m) ** 12 - 1) * 100.0 if irr_m is not None else 0.0 |
|
|
|
return { |
|
"npv": npv, |
|
"total_5yr_roi": roi5y, |
|
"payback_months": payback_m, |
|
"payback_years": (payback_m / 12.0) if not math.isinf(payback_m) else float("inf"), |
|
"irr_annual_pct": irr_annual, |
|
"months": months, |
|
"cum_values": cum_series, |
|
"delta_monthly": delta_monthly, |
|
"country_data": country_data |
|
} |
|
|
|
|
|
|
|
|
|
|
|
def create_enhanced_cashflow_chart(months, cum_values, payback_month=None, sensitivity_data=None): |
|
"""Расширенный график с доверительными интервалами""" |
|
fig = go.Figure() |
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
x=months, y=cum_values, mode="lines", |
|
line=dict(width=4, color="#2563EB"), |
|
fill='tozeroy', fillcolor="rgba(37,99,235,0.1)", |
|
name="Cumulative ΔCF" |
|
)) |
|
|
|
|
|
if sensitivity_data: |
|
upper_bound = [val * 1.2 for val in cum_values] |
|
lower_bound = [val * 0.8 for val in cum_values] |
|
|
|
fig.add_trace(go.Scatter( |
|
x=months + months[::-1], |
|
y=upper_bound + lower_bound[::-1], |
|
fill='toself', |
|
fillcolor='rgba(37,99,235,0.1)', |
|
line=dict(color='rgba(255,255,255,0)'), |
|
name='Confidence Interval' |
|
)) |
|
|
|
fig.add_hline(y=0, line_width=2, line_dash="dot", line_color="#94A3B8") |
|
|
|
if payback_month not in (None, float("inf")): |
|
fig.add_vline( |
|
x=payback_month, line_width=2, line_dash="dash", line_color="#10B981", |
|
annotation_text="💰 Break-even", annotation_position="top right" |
|
) |
|
|
|
fig.update_layout( |
|
title="Enhanced Cashflow Projection with Confidence Intervals", |
|
margin=dict(l=20, r=20, t=50, b=20), |
|
xaxis_title="Month", |
|
yaxis_title="Cumulative €", |
|
plot_bgcolor="#FFFFFF", |
|
paper_bgcolor="#FFFFFF", |
|
showlegend=True, |
|
height=420 |
|
) |
|
|
|
return fig |
|
|
|
def create_sensitivity_chart(sensitivity_data): |
|
"""График анализа чувствительности""" |
|
params = list(sensitivity_data.keys()) |
|
high_impacts = [sensitivity_data[p]["high"] for p in params] |
|
low_impacts = [sensitivity_data[p]["low"] for p in params] |
|
|
|
fig = go.Figure() |
|
|
|
fig.add_trace(go.Bar( |
|
name='Upside (+20%)', |
|
x=params, |
|
y=high_impacts, |
|
marker_color='#10B981' |
|
)) |
|
|
|
fig.add_trace(go.Bar( |
|
name='Downside (-20%)', |
|
x=params, |
|
y=low_impacts, |
|
marker_color='#EF4444' |
|
)) |
|
|
|
fig.update_layout( |
|
title="Sensitivity Analysis: Impact on ROI", |
|
xaxis_title="Parameter", |
|
yaxis_title="ROI Impact (%)", |
|
barmode='group', |
|
height=350 |
|
) |
|
|
|
return fig |
|
|
|
def create_comparison_radar_chart(countries_data): |
|
"""Радарная диаграмма для сравнения стран""" |
|
categories = ['ROI', 'Market Growth', 'Ease of Business', 'Tax Efficiency', 'Banking Score'] |
|
|
|
fig = go.Figure() |
|
|
|
for country, data in countries_data.items(): |
|
values = [ |
|
data.get('roi_normalized', 50), |
|
data.get('market_growth', 0) * 10, |
|
data.get('ease_business', 0) * 10, |
|
100 - (data.get('total_tax_rate', 50)), |
|
data.get('banking_score', 0) * 10 |
|
] |
|
|
|
fig.add_trace(go.Scatterpolar( |
|
r=values, |
|
theta=categories, |
|
fill='toself', |
|
name=country |
|
)) |
|
|
|
fig.update_layout( |
|
polar=dict( |
|
radialaxis=dict(visible=True, range=[0, 100]) |
|
), |
|
showlegend=True, |
|
title="Multi-Dimensional Country Comparison" |
|
) |
|
|
|
return fig |
|
|
|
|
|
|
|
|
|
|
|
def render_enhanced_kpis(result, country_data, monte_carlo_data=None): |
|
"""Расширенные KPI с дополнительной информацией""" |
|
payback_years = "Never" if result["payback_years"] == float("inf") else f'{result["payback_years"]:.1f} years' |
|
roi_str = f'{result["total_5yr_roi"]:.1f}%' |
|
irr_str = f'{result["irr_annual_pct"]:.1f}%' |
|
npv_str = f'€{result["npv"]:,.0f}' |
|
|
|
|
|
risk_class = "good" if result["payback_years"] <= 1.5 else ("warn" if result["payback_years"] <= 3.0 else "") |
|
|
|
k1 = f""" |
|
<div class="vt-kpi {risk_class}"> |
|
<div class="label">💰 Payback Period</div> |
|
<div class="value">{payback_years}</div> |
|
<div class="vt-note">Time to break even • Market growth: {country_data.get('market_growth', 'N/A')}%</div> |
|
</div>""" |
|
|
|
k2 = f""" |
|
<div class="vt-kpi {'good' if result['total_5yr_roi'] >= 100 else ''}"> |
|
<div class="label">🚀 ROI (5-year)</div> |
|
<div class="value">{roi_str}</div> |
|
<div class="vt-note">Risk-adjusted return • {country_data.get('tax_treaties', 'N/A')} tax treaties</div> |
|
</div>""" |
|
|
|
k3 = f""" |
|
<div class="vt-kpi"> |
|
<div class="label">📈 IRR (Annual)</div> |
|
<div class="value">{irr_str}</div> |
|
<div class="vt-note">Internal rate of return • Ease of business: {country_data.get('ease_business', 'N/A')}/10</div> |
|
</div>""" |
|
|
|
|
|
mc_info = "" |
|
if monte_carlo_data: |
|
mc_info = f" • 90% confidence: €{monte_carlo_data.get('percentile_10', 0):,.0f} - €{monte_carlo_data.get('percentile_90', 0):,.0f}" |
|
|
|
k4 = f""" |
|
<div class="vt-kpi"> |
|
<div class="label">💎 NPV</div> |
|
<div class="value">{npv_str}</div> |
|
<div class="vt-note">Net present value{mc_info}</div> |
|
</div>""" |
|
|
|
return k1, k2, k3, k4 |
|
|
|
def render_risk_assessment(result, country_data): |
|
"""Генерация оценки рисков""" |
|
risks = [] |
|
|
|
if result["payback_years"] > 3: |
|
risks.append('<span class="risk-indicator risk-high">⚠️ Long Payback</span>') |
|
elif result["payback_years"] <= 1.5: |
|
risks.append('<span class="risk-indicator risk-low">✅ Quick Payback</span>') |
|
else: |
|
risks.append('<span class="risk-indicator risk-medium">⏳ Moderate Payback</span>') |
|
|
|
if country_data.get('ease_business', 0) >= 8: |
|
risks.append('<span class="risk-indicator risk-low">🏢 Business Friendly</span>') |
|
elif country_data.get('ease_business', 0) < 7: |
|
risks.append('<span class="risk-indicator risk-medium">📋 Complex Setup</span>') |
|
|
|
if country_data.get('inflation', 0) > 4: |
|
risks.append('<span class="risk-indicator risk-medium">📊 High Inflation</span>') |
|
else: |
|
risks.append('<span class="risk-indicator risk-low">💹 Stable Economy</span>') |
|
|
|
return "".join(risks) |
|
|
|
def create_executive_summary(result, country, recommendations): |
|
"""Создание краткого резюме для руководителей""" |
|
summary = f""" |
|
<div class="ai-recommendation"> |
|
<h3 style="margin: 0 0 16px 0; color: #6366F1;">🎯 Executive Summary: {country}</h3> |
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;"> |
|
<div> |
|
<strong>💰 Financial Impact</strong><br/> |
|
ROI: {result['total_5yr_roi']:.1f}%<br/> |
|
Payback: {result['payback_years']:.1f} years |
|
</div> |
|
<div> |
|
<strong>⚡ Key Insights</strong><br/> |
|
IRR: {result['irr_annual_pct']:.1f}%<br/> |
|
NPV: €{result['npv']:,.0f} |
|
</div> |
|
</div> |
|
<div style="margin-top: 16px;"> |
|
<strong>🤖 AI Recommendations:</strong> |
|
<ul style="margin: 8px 0; padding-left: 20px;"> |
|
""" |
|
|
|
for rec in recommendations: |
|
summary += f"<li>{rec}</li>" |
|
|
|
summary += """ |
|
</ul> |
|
</div> |
|
</div> |
|
""" |
|
|
|
return summary |
|
|
|
|
|
|
|
|
|
|
|
def create_enhanced_app(): |
|
with gr.Blocks(css=CSS, theme=THEME, title="VisaTier - Enhanced Immigration ROI Simulator") as demo: |
|
|
|
|
|
gr.HTML(""" |
|
<div class="vt-header"> |
|
<div> |
|
<div class="title">🌍 VisaTier — Enhanced Immigration ROI Simulator</div> |
|
<div style="font-size: 14px; color: #CBD5E1; margin-top: 4px;"> |
|
Advanced analytics • Risk modeling • AI insights |
|
</div> |
|
</div> |
|
<div class="right"> |
|
<div>Built for founders & investors</div> |
|
<div style="font-size: 10px;">v2.0 Enhanced</div> |
|
</div> |
|
</div> |
|
""") |
|
|
|
|
|
with gr.Accordion("📊 Enhanced Methodology & Features", open=False): |
|
gr.HTML(""" |
|
<div class="vt-method"> |
|
<h4>🔬 Advanced Features Added:</h4> |
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; margin: 12px 0;"> |
|
<div> |
|
<strong>📈 Risk Analytics</strong> |
|
<ul style="font-size: 13px; margin: 8px 0; padding-left: 16px;"> |
|
<li>Monte Carlo simulations</li> |
|
<li>Sensitivity analysis</li> |
|
<li>Confidence intervals</li> |
|
</ul> |
|
</div> |
|
<div> |
|
<strong>🎯 Smart Insights</strong> |
|
<ul style="font-size: 13px; margin: 8px 0; padding-left: 16px;"> |
|
<li>AI-powered recommendations</li> |
|
<li>Multi-currency support</li> |
|
<li>Inflation adjustments</li> |
|
</ul> |
|
</div> |
|
<div> |
|
<strong>🚀 Enhanced UX</strong> |
|
<ul style="font-size: 13px; margin: 8px 0; padding-left: 16px;"> |
|
<li>Visa pathway guidance</li> |
|
<li>Export capabilities</li> |
|
<li>Mobile optimization</li> |
|
</ul> |
|
</div> |
|
</div> |
|
</div> |
|
""") |
|
|
|
|
|
gr.HTML(""" |
|
<div class="vt-hero"> |
|
<h2 style="margin:0; font-size:24px; font-weight:800; color:#0F172A;"> |
|
🎯 Model the complete financial impact of your relocation |
|
</h2> |
|
<p style="margin:8px 0 12px; color:#334155; font-size:16px;"> |
|
Advanced simulation with risk modeling, AI insights, and multi-dimensional analysis |
|
</p> |
|
<div style="margin-top: 16px;"> |
|
<span class="vt-badge">✨ AI-Powered</span> |
|
<span class="vt-badge">📊 Risk Analytics</span> |
|
<span class="vt-badge">💼 Executive Ready</span> |
|
<span class="vt-badge">📱 Mobile Friendly</span> |
|
</div> |
|
</div> |
|
""") |
|
|
|
|
|
with gr.Row(): |
|
with gr.Column(scale=5): |
|
gr.Markdown("### 🎛️ Simulation Parameters") |
|
|
|
with gr.Row(): |
|
dest = gr.Dropdown( |
|
list(COUNTRY_CONFIG.keys()), |
|
value="UAE (Dubai)", |
|
label="🎯 Destination Country", |
|
info="Select your target destination" |
|
) |
|
currency_display = gr.Dropdown( |
|
list(CURRENCIES.keys()), |
|
value="EUR", |
|
label="💱 Display Currency", |
|
info="All amounts will be converted" |
|
) |
|
|
|
with gr.Accordion("💼 Current Business Situation", open=True): |
|
with gr.Row(): |
|
rev0 = gr.Number( |
|
value=30000, |
|
label="📈 Monthly Revenue (€)", |
|
info="Your current monthly business revenue" |
|
) |
|
margin0 = gr.Slider( |
|
value=25, minimum=1, maximum=70, step=1, |
|
label="💰 EBITDA Margin (%)", |
|
info="Current profit margin before taxes" |
|
) |
|
|
|
with gr.Row(): |
|
corp0 = gr.Slider( |
|
value=20, minimum=0, maximum=50, step=1, |
|
label="🏢 Corporate Tax Rate (%)", |
|
info="Current corporate tax burden" |
|
) |
|
pers0 = gr.Slider( |
|
value=10, minimum=0, maximum=50, step=1, |
|
label="👤 Personal Tax Rate (%)", |
|
info="Current personal tax on distributions" |
|
) |
|
|
|
with gr.Row(): |
|
living0 = gr.Number( |
|
value=4000, |
|
label="🏠 Living Costs (€/month)", |
|
info="Current monthly living expenses" |
|
) |
|
ongoing0 = gr.Number( |
|
value=0, |
|
label="⚙️ Other Costs (€/month)", |
|
info="Additional monthly business costs" |
|
) |
|
|
|
with gr.Accordion("🚀 Destination Projections", open=True): |
|
gr.Markdown("*Adjustments for your new destination:*") |
|
|
|
with gr.Row(): |
|
rev_mult = gr.Slider( |
|
value=3.0, minimum=0.5, maximum=5.0, step=0.1, |
|
label="📊 Revenue Multiplier (×)", |
|
info="Expected revenue growth factor" |
|
) |
|
margin_delta = gr.Slider( |
|
value=5.0, minimum=-20, maximum=30, step=0.5, |
|
label="📈 Margin Improvement (pp)", |
|
info="Expected margin change in percentage points" |
|
) |
|
|
|
with gr.Row(): |
|
corp1 = gr.Slider( |
|
value=9, minimum=0, maximum=50, step=1, |
|
label="🏢 New Corporate Tax (%)", |
|
info="Destination corporate tax rate" |
|
) |
|
pers1 = gr.Slider( |
|
value=0, minimum=0, maximum=50, step=1, |
|
label="👤 New Personal Tax (%)", |
|
info="Destination personal tax rate" |
|
) |
|
|
|
with gr.Row(): |
|
living1 = gr.Number( |
|
value=9000, |
|
label="🏠 New Living Costs (€/month)", |
|
info="Expected monthly living costs in destination" |
|
) |
|
ongoing1 = gr.Number( |
|
value=1500, |
|
label="⚙️ New Ongoing Costs (€/month)", |
|
info="Additional monthly business costs in destination" |
|
) |
|
|
|
with gr.Accordion("🎯 Investment & Risk Parameters", open=False): |
|
with gr.Row(): |
|
capex_once = gr.Number( |
|
value=35000, |
|
label="💸 Setup Investment (€)", |
|
info="One-time relocation and setup costs" |
|
) |
|
horizon_m = gr.Slider( |
|
value=60, minimum=12, maximum=120, step=1, |
|
label="📅 Analysis Period (months)", |
|
info="Time horizon for analysis" |
|
) |
|
|
|
with gr.Row(): |
|
discount_a = gr.Slider( |
|
value=12, minimum=0, maximum=40, step=1, |
|
label="💹 Discount Rate (%)", |
|
info="Your required annual rate of return" |
|
) |
|
success = gr.Slider( |
|
value=75, minimum=10, maximum=100, step=1, |
|
label="🎯 Success Probability (%)", |
|
info="Likelihood of achieving projections" |
|
) |
|
|
|
with gr.Row(): |
|
include_inflation = gr.Checkbox( |
|
value=True, |
|
label="📊 Include Inflation Impact", |
|
info="Adjust for inflation in destination country" |
|
) |
|
include_additional = gr.Checkbox( |
|
value=True, |
|
label="💼 Include Additional Costs", |
|
info="Factor in visa renewals, compliance costs, etc." |
|
) |
|
|
|
with gr.Row(): |
|
estimate_btn = gr.Button( |
|
"🚀 Run Enhanced Analysis", |
|
variant="primary", |
|
size="lg" |
|
) |
|
reset_btn = gr.Button( |
|
"🔄 Reset to Defaults", |
|
variant="secondary" |
|
) |
|
|
|
with gr.Column(scale=7): |
|
gr.Markdown("### 📊 Analysis Results") |
|
|
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
kpi1 = gr.HTML(""" |
|
<div class="vt-kpi"> |
|
<div class="label">💰 Payback Period</div> |
|
<div class="value">—</div> |
|
<div class="vt-note">Click "Run Analysis" to calculate</div> |
|
</div>""") |
|
with gr.Column(scale=1): |
|
kpi2 = gr.HTML(""" |
|
<div class="vt-kpi"> |
|
<div class="label">🚀 ROI (5-year)</div> |
|
<div class="value">—</div> |
|
<div class="vt-note">Risk-adjusted return</div> |
|
</div>""") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
kpi3 = gr.HTML(""" |
|
<div class="vt-kpi"> |
|
<div class="label">📈 IRR (Annual)</div> |
|
<div class="value">—</div> |
|
<div class="vt-note">Internal rate of return</div> |
|
</div>""", visible=False) |
|
with gr.Column(scale=1): |
|
kpi4 = gr.HTML(""" |
|
<div class="vt-kpi"> |
|
<div class="label">💎 NPV</div> |
|
<div class="value">—</div> |
|
<div class="vt-note">Net present value</div> |
|
</div>""", visible=False) |
|
|
|
|
|
risk_indicators = gr.HTML("", visible=False) |
|
|
|
|
|
executive_summary = gr.HTML("", visible=False) |
|
|
|
|
|
main_chart = gr.Plot(visible=False) |
|
|
|
with gr.Row(): |
|
sensitivity_chart = gr.Plot(visible=False) |
|
monte_carlo_info = gr.HTML("", visible=False) |
|
|
|
|
|
state_result = gr.State(value=None) |
|
state_enhanced_data = gr.State(value=None) |
|
|
|
|
|
def run_enhanced_analysis( |
|
dest, currency_display, rev0, margin0, corp0, pers0, living0, ongoing0, |
|
rev_mult, margin_delta, corp1, pers1, living1, ongoing1, |
|
capex_once, horizon_m, discount_a, success, |
|
include_inflation, include_additional |
|
): |
|
try: |
|
|
|
if dest in COUNTRY_CONFIG: |
|
cfg = COUNTRY_CONFIG[dest] |
|
|
|
|
|
result = compute_enhanced_monthly_delta_cashflow( |
|
rev0, margin0, corp0, pers0, living0, ongoing0, |
|
dest, rev_mult, margin_delta, corp1, pers1, living1, ongoing1, |
|
capex_once, int(horizon_m), discount_a, success, |
|
include_inflation, include_additional |
|
) |
|
|
|
|
|
base_params = { |
|
'rev_mult': rev_mult, |
|
'margin_delta_pp': margin_delta, |
|
'success_pct': success, |
|
'base_roi': result['total_5yr_roi'] |
|
} |
|
|
|
sensitivity_data = calculate_sensitivity_analysis(base_params, result) |
|
monte_carlo_data = create_monte_carlo_simulation(base_params, 1000) |
|
|
|
|
|
personal_profile = { |
|
'revenue': rev0, |
|
'risk_tolerance': success |
|
} |
|
recommendations = generate_ai_recommendation(result, dest, personal_profile) |
|
|
|
|
|
country_data = COUNTRY_CONFIG.get(dest, {}) |
|
k1, k2, k3, k4 = render_enhanced_kpis(result, country_data, monte_carlo_data) |
|
|
|
risk_html = render_risk_assessment(result, country_data) |
|
exec_summary = create_executive_summary(result, dest, recommendations) |
|
|
|
|
|
main_chart_fig = create_enhanced_cashflow_chart( |
|
result["months"], result["cum_values"], |
|
result["payback_months"], sensitivity_data |
|
) |
|
|
|
sensitivity_chart_fig = create_sensitivity_chart(sensitivity_data) |
|
|
|
|
|
mc_html = f""" |
|
<div class="vt-kpi"> |
|
<div class="label">🎲 Monte Carlo Analysis</div> |
|
<div class="value">{monte_carlo_data['probability_positive']:.1f}%</div> |
|
<div class="vt-note"> |
|
Probability of positive outcome<br/> |
|
Expected range: €{monte_carlo_data['percentile_10']:,.0f} - €{monte_carlo_data['percentile_90']:,.0f} |
|
</div> |
|
</div> |
|
""" |
|
|
|
enhanced_data = { |
|
'result': result, |
|
'sensitivity': sensitivity_data, |
|
'monte_carlo': monte_carlo_data, |
|
'recommendations': recommendations, |
|
'country_data': country_data |
|
} |
|
|
|
return ( |
|
k1, k2, |
|
gr.update(value=k3, visible=True), |
|
gr.update(value=k4, visible=True), |
|
gr.update(value=risk_html, visible=True), |
|
gr.update(value=exec_summary, visible=True), |
|
gr.update(value=main_chart_fig, visible=True), |
|
gr.update(value=sensitivity_chart_fig, visible=True), |
|
gr.update(value=mc_html, visible=True), |
|
enhanced_data, result |
|
) |
|
|
|
except Exception as e: |
|
error_msg = f"❌ Analysis failed: {str(e)}" |
|
return ( |
|
f'<div class="vt-kpi warn"><div class="value">{error_msg}</div></div>', |
|
"", gr.update(visible=False), gr.update(visible=False), |
|
gr.update(visible=False), gr.update(visible=False), |
|
gr.update(visible=False), gr.update(visible=False), |
|
gr.update(visible=False), None, None |
|
) |
|
|
|
estimate_btn.click( |
|
run_enhanced_analysis, |
|
inputs=[ |
|
dest, currency_display, rev0, margin0, corp0, pers0, living0, ongoing0, |
|
rev_mult, margin_delta, corp1, pers1, living1, ongoing1, |
|
capex_once, horizon_m, discount_a, success, |
|
include_inflation, include_additional |
|
], |
|
outputs=[ |
|
kpi1, kpi2, kpi3, kpi4, risk_indicators, executive_summary, |
|
main_chart, sensitivity_chart, monte_carlo_info, |
|
state_enhanced_data, state_result |
|
] |
|
) |
|
|
|
|
|
gr.Markdown("## 🛂 Visa Pathway Guidance") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
visa_country = gr.Dropdown( |
|
list(VISA_TYPES.keys()), |
|
value="UAE (Dubai)", |
|
label="Select Country for Visa Info" |
|
) |
|
get_visa_info_btn = gr.Button("Get Visa Information", variant="secondary") |
|
|
|
with gr.Column(scale=2): |
|
visa_timeline = gr.HTML("") |
|
|
|
def show_visa_info(country): |
|
if country in VISA_TYPES: |
|
timeline = create_enhanced_timeline(country) |
|
return timeline |
|
return "No visa information available for this country." |
|
|
|
get_visa_info_btn.click( |
|
show_visa_info, |
|
inputs=[visa_country], |
|
outputs=[visa_timeline] |
|
) |
|
|
|
|
|
gr.Markdown("## 🌍 Enhanced Multi-Country Comparison") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=5): |
|
gr.Markdown("**Shared assumptions for comparison:**") |
|
|
|
with gr.Row(): |
|
rev0_c = gr.Number(value=30000, label="📈 Baseline Revenue (€/month)") |
|
margin0_c = gr.Slider(value=25, minimum=1, maximum=70, step=1, label="💰 Baseline Margin (%)") |
|
|
|
with gr.Row(): |
|
corp0_c = gr.Slider(value=20, minimum=0, maximum=50, step=1, label="🏢 Current Corp Tax (%)") |
|
pers0_c = gr.Slider(value=10, minimum=0, maximum=50, step=1, label="👤 Current Personal Tax (%)") |
|
|
|
with gr.Row(): |
|
living0_c = gr.Number(value=4000, label="🏠 Current Living (€/month)") |
|
success_c = gr.Slider(value=75, minimum=10, maximum=100, step=1, label="🎯 Success Probability (%)") |
|
|
|
compare_enhanced_btn = gr.Button("🚀 Run Enhanced Comparison", variant="primary") |
|
|
|
with gr.Column(scale=7): |
|
comparison_results = gr.HTML("") |
|
|
|
with gr.Row(): |
|
comparison_bar_chart = gr.Plot() |
|
radar_comparison = gr.Plot() |
|
|
|
def run_enhanced_comparison(rev0, margin0, corp0, pers0, living0, success): |
|
try: |
|
countries_data = {} |
|
results_for_chart = [] |
|
|
|
for country_name in COUNTRY_CONFIG.keys(): |
|
cfg = COUNTRY_CONFIG[country_name] |
|
|
|
result = compute_enhanced_monthly_delta_cashflow( |
|
rev0, margin0, corp0, pers0, living0, 0, |
|
country_name, cfg["rev_mult"], cfg["margin_delta_pp"], |
|
cfg["corp_tax"]*100, cfg["pers_tax"]*100, |
|
cfg["living_month"], cfg["ongoing_month"], |
|
cfg["setup_once"], 60, 12, success, |
|
True, True |
|
) |
|
|
|
countries_data[country_name] = { |
|
'roi_normalized': max(0, min(100, result['total_5yr_roi'])), |
|
'market_growth': cfg.get('market_growth', 0), |
|
'ease_business': cfg.get('ease_business', 0), |
|
'total_tax_rate': (cfg['corp_tax'] + cfg['pers_tax']) * 100, |
|
'banking_score': cfg.get('banking_score', 0) |
|
} |
|
|
|
results_for_chart.append({ |
|
'country': country_name, |
|
'roi': result['total_5yr_roi'], |
|
'payback': result['payback_years'], |
|
'npv': result['npv'] |
|
}) |
|
|
|
|
|
table_html = """ |
|
<div class="comparison-enhanced"> |
|
<h3>🎯 Comprehensive Country Analysis</h3> |
|
<div class="table-like"> |
|
<table> |
|
<tr> |
|
<th>Country</th> |
|
<th>ROI (5y)</th> |
|
<th>Payback</th> |
|
<th>Market Growth</th> |
|
<th>Ease of Business</th> |
|
<th>Recommendation</th> |
|
</tr> |
|
""" |
|
|
|
for result_data in results_for_chart: |
|
country = result_data['country'] |
|
cfg = COUNTRY_CONFIG[country] |
|
payback_str = "Never" if result_data['payback'] == float('inf') else f"{result_data['payback']:.1f}y" |
|
|
|
|
|
if result_data['roi'] > 150 and result_data['payback'] < 2: |
|
recommendation = "🚀 Excellent" |
|
rec_class = "risk-low" |
|
elif result_data['roi'] > 75 and result_data['payback'] < 3: |
|
recommendation = "✅ Good" |
|
rec_class = "risk-medium" |
|
else: |
|
recommendation = "⚠️ Consider" |
|
rec_class = "risk-high" |
|
|
|
table_html += f""" |
|
<tr> |
|
<td><strong>{country}</strong></td> |
|
<td>{result_data['roi']:.1f}%</td> |
|
<td>{payback_str}</td> |
|
<td>{cfg.get('market_growth', 0):.1f}%</td> |
|
<td>{cfg.get('ease_business', 0):.1f}/10</td> |
|
<td><span class="risk-indicator {rec_class}">{recommendation}</span></td> |
|
</tr> |
|
""" |
|
|
|
table_html += """ |
|
</table> |
|
</div> |
|
</div> |
|
""" |
|
|
|
|
|
countries = [r['country'] for r in results_for_chart] |
|
rois = [r['roi'] for r in results_for_chart] |
|
|
|
bar_fig = go.Figure() |
|
bar_fig.add_bar( |
|
x=countries, y=rois, |
|
marker_color=['#10B981' if roi > 100 else '#F59E0B' if roi > 50 else '#EF4444' for roi in rois], |
|
text=[f'{roi:.1f}%' for roi in rois], |
|
textposition='auto' |
|
) |
|
bar_fig.update_layout( |
|
title="ROI Comparison Across Countries", |
|
xaxis_title="Country", |
|
yaxis_title="5-Year ROI (%)", |
|
height=400 |
|
) |
|
|
|
|
|
radar_fig = create_comparison_radar_chart(countries_data) |
|
|
|
return table_html, bar_fig, radar_fig |
|
|
|
except Exception as e: |
|
error_html = f""" |
|
<div class="vt-kpi warn"> |
|
<div class="value">❌ Comparison failed: {str(e)}</div> |
|
</div> |
|
""" |
|
return error_html, go.Figure(), go.Figure() |
|
|
|
compare_enhanced_btn.click( |
|
run_enhanced_comparison, |
|
inputs=[rev0_c, margin0_c, corp0_c, pers0_c, living0_c, success_c], |
|
outputs=[comparison_results, comparison_bar_chart, radar_comparison] |
|
) |
|
|
|
|
|
gr.Markdown("## 📁 Export & Share Results") |
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
export_format = gr.Dropdown( |
|
["JSON", "PDF Report", "Excel Summary"], |
|
value="JSON", |
|
label="Export Format" |
|
) |
|
|
|
export_btn = gr.Button("📥 Generate Export", variant="secondary") |
|
|
|
export_output = gr.File(label="Download Results", visible=False) |
|
export_status = gr.HTML("") |
|
|
|
def generate_export(format_type, enhanced_data, current_result): |
|
if not enhanced_data or not current_result: |
|
return gr.update(visible=False), "❌ No data to export. Please run analysis first." |
|
|
|
try: |
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
|
|
if format_type == "JSON": |
|
|
|
export_data = { |
|
"meta": { |
|
"timestamp": datetime.now().isoformat(), |
|
"version": "2.0_enhanced", |
|
"generated_by": "VisaTier Immigration ROI Simulator" |
|
}, |
|
"analysis_results": { |
|
"npv": current_result["npv"], |
|
"roi_5y": current_result["total_5yr_roi"], |
|
"payback_years": current_result["payback_years"], |
|
"irr_annual": current_result["irr_annual_pct"] |
|
}, |
|
"risk_analytics": { |
|
"sensitivity_analysis": enhanced_data.get('sensitivity', {}), |
|
"monte_carlo": enhanced_data.get('monte_carlo', {}), |
|
}, |
|
"recommendations": enhanced_data.get('recommendations', []), |
|
"country_data": enhanced_data.get('country_data', {}) |
|
} |
|
|
|
|
|
json_str = json.dumps(export_data, indent=2, ensure_ascii=False) |
|
filename = f"immigration_roi_analysis_{timestamp}.json" |
|
|
|
|
|
with open(filename, 'w', encoding='utf-8') as f: |
|
f.write(json_str) |
|
|
|
return gr.update(value=filename, visible=True), "✅ Export generated successfully!" |
|
|
|
else: |
|
return gr.update(visible=False), f"⚠️ {format_type} export coming soon!" |
|
|
|
except Exception as e: |
|
return gr.update(visible=False), f"❌ Export failed: {str(e)}" |
|
|
|
export_btn.click( |
|
generate_export, |
|
inputs=[export_format, state_enhanced_data, state_result], |
|
outputs=[export_output, export_status] |
|
) |
|
|
|
|
|
gr.HTML(f""" |
|
<div class="vt-footer"> |
|
<div style="margin-bottom: 12px;"> |
|
<strong>VisaTier Enhanced Immigration ROI Simulator v2.0</strong> |
|
</div> |
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 16px;"> |
|
<div> |
|
<strong>🚀 New Features</strong><br/> |
|
<small>AI recommendations, risk analytics, enhanced UX</small> |
|
</div> |
|
<div> |
|
<strong>📊 Analytics</strong><br/> |
|
<small>Monte Carlo, sensitivity analysis, multi-currency</small> |
|
</div> |
|
<div> |
|
<strong>🌍 Coverage</strong><br/> |
|
<small>5 countries, 15+ visa types, 100+ data points</small> |
|
</div> |
|
</div> |
|
<div style="text-align: center; font-size: 12px; color: #64748B;"> |
|
© 2025 VisaTier — Enhanced Immigration & Investment Advisory<br/> |
|
<a href="https://visatier.com" target="_blank" style="color: #2563EB;">visatier.com</a> • |
|
<a href="https://calendly.com/your-team/diagnostic" target="_blank" style="color: #2563EB;">📅 Book Enhanced Diagnostic</a> • |
|
<a href="mailto:support@visatier.com" style="color: #2563EB;">✉️ Support</a> |
|
</div> |
|
<div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid #E2E8F0; font-size: 11px; color: #94A3B8; text-align: center;"> |
|
⚠️ Enhanced model for illustrative purposes. Not financial, tax, or legal advice. |
|
Results depend on execution, market conditions, and personal circumstances. |
|
Always consult qualified professionals for personalized guidance. |
|
</div> |
|
</div> |
|
""") |
|
|
|
return demo |
|
|
|
|
|
def reset_all_inputs(): |
|
"""Функция сброса всех параметров к значениям по умолчанию""" |
|
return ( |
|
"UAE (Dubai)", |
|
"EUR", |
|
30000, |
|
25, |
|
20, |
|
10, |
|
4000, |
|
0, |
|
3.0, |
|
5.0, |
|
9, |
|
0, |
|
9000, |
|
1500, |
|
35000, |
|
60, |
|
12, |
|
75, |
|
True, |
|
True |
|
) |
|
|
|
|
|
demo = create_enhanced_app() |
|
|
|
if __name__ == "__main__": |
|
demo.launch( |
|
share=False, |
|
server_name="0.0.0.0", |
|
server_port=7860, |
|
show_api=False, |
|
show_error=True |
|
) |