CatherineYefi's picture
Update app.py
98bf0ca verified
# app.py
# VisaTier — Immigration ROI Simulator (Enhanced Version)
# Добавлены: чувствительность, экспорт, визы, налоги, валютность, AI-рекомендации
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 = {}
# Тестируем изменения ключевых параметров на ±20%
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)
# Упрощенный расчет ROI для симуляции
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 для скачивания
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: # расходы на продление визы каждые 3 года
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
# =========================
# UI HELPERS (расширенные)
# =========================
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>"""
# Добавляем Monte Carlo данные если есть
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:
# HEADER с дополнительной информацией
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>
""")
# HERO расширенный
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>
""")
# ===== MAIN SIMULATOR (ENHANCED) =====
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")
# KPIs Grid
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 Assessment
risk_indicators = gr.HTML("", visible=False)
# Executive Summary
executive_summary = gr.HTML("", visible=False)
# Charts
main_chart = gr.Plot(visible=False)
with gr.Row():
sensitivity_chart = gr.Plot(visible=False)
monte_carlo_info = gr.HTML("", visible=False)
# State variables
state_result = gr.State(value=None)
state_enhanced_data = gr.State(value=None)
# Main calculation function
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:
# Apply template defaults intelligently
if dest in COUNTRY_CONFIG:
cfg = COUNTRY_CONFIG[dest]
# Run main calculation
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
)
# Enhanced analytics
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)
# AI recommendations
personal_profile = {
'revenue': rev0,
'risk_tolerance': success
}
recommendations = generate_ai_recommendation(result, dest, personal_profile)
# Render components
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)
# Charts
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)
# Monte Carlo summary
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, # First row KPIs (always visible)
gr.update(value=k3, visible=True), # Second row KPIs
gr.update(value=k4, visible=True),
gr.update(value=risk_html, visible=True), # Risk indicators
gr.update(value=exec_summary, visible=True), # Executive summary
gr.update(value=main_chart_fig, visible=True), # Main chart
gr.update(value=sensitivity_chart_fig, visible=True), # Sensitivity
gr.update(value=mc_html, visible=True), # Monte Carlo
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
]
)
# ===== VISA GUIDANCE SECTION =====
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]
)
# ===== ENHANCED COMPARISON SECTION =====
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']
})
# Create comparison table
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"
# Simple recommendation logic
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>
"""
# Create bar chart
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
)
# Create radar chart
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]
)
# ===== EXPORT SECTION =====
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":
# Create comprehensive JSON export
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', {})
}
# Create file
json_str = json.dumps(export_data, indent=2, ensure_ascii=False)
filename = f"immigration_roi_analysis_{timestamp}.json"
# Save to temporary file
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]
)
# ===== ENHANCED FOOTER =====
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
# ===== RESET FUNCTIONALITY =====
def reset_all_inputs():
"""Функция сброса всех параметров к значениям по умолчанию"""
return (
"UAE (Dubai)", # dest
"EUR", # currency
30000, # rev0
25, # margin0
20, # corp0
10, # pers0
4000, # living0
0, # ongoing0
3.0, # rev_mult
5.0, # margin_delta
9, # corp1
0, # pers1
9000, # living1
1500, # ongoing1
35000, # capex_once
60, # horizon_m
12, # discount_a
75, # success
True, # include_inflation
True # include_additional
)
# Главная переменная для HuggingFace
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
)