Spaces:
Paused
Paused
Phakamas
Fix Starlette TemplateResponse compatibility: pin starlette<0.39.0, fastapi<0.115.0 and patch TemplateResponse
9f96099 Download app.py from Phakamas/Financial-OS: direct link, hf CLI and curl.
- Browser
- Download file 49.1 kB
-
https://huggingface.co/spaces/Phakamas/Financial-OS/resolve/main/app.py
- Command line
-
hf download hf://spaces/Phakamas/Financial-OS/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Phakamas/Financial-OS/resolve/main/app.py
49.1 kB
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Financial-OS v7.0 โ World-Class AI Banking Platform | |
| # Hugging Face Spaces ZeroGPU Edition | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| from __future__ import annotations | |
| import io | |
| import os | |
| import textwrap | |
| import warnings | |
| import datetime as dt | |
| import tempfile | |
| from dataclasses import dataclass, field | |
| from typing import Dict, List, Protocol, Tuple | |
| from contextlib import nullcontext | |
| import numpy as np | |
| import pandas as pd | |
| try: | |
| import torch | |
| import torch.nn as nn | |
| except ImportError: | |
| torch = None | |
| nn = None | |
| from sklearn.decomposition import PCA | |
| from sklearn.preprocessing import StandardScaler | |
| from sklearn.model_selection import TimeSeriesSplit | |
| import xgboost as xgb | |
| import lightgbm as lgb | |
| import gradio as gr | |
| try: | |
| import gradio_client.utils as _gc_utils | |
| _orig_get_type = _gc_utils.get_type | |
| def _safe_get_type(schema): | |
| if isinstance(schema, bool): | |
| return "bool" | |
| return _orig_get_type(schema) | |
| _gc_utils.get_type = _safe_get_type | |
| _orig_json_schema = _gc_utils._json_schema_to_python_type | |
| def _safe_json_schema_to_python_type(schema, defs=None): | |
| if isinstance(schema, bool): | |
| return "bool" | |
| return _orig_json_schema(schema, defs) | |
| _gc_utils._json_schema_to_python_type = _safe_json_schema_to_python_type | |
| except Exception: | |
| pass | |
| try: | |
| from starlette.templating import Jinja2Templates | |
| _orig_template_response = Jinja2Templates.TemplateResponse | |
| def _safe_template_response(self, *args, **kwargs): | |
| if len(args) >= 2 and isinstance(args[0], str) and isinstance(args[1], dict): | |
| name = args[0] | |
| context = args[1] | |
| request = context.get("request") | |
| if request is not None: | |
| return _orig_template_response(self, request=request, name=name, context=context, **kwargs) | |
| return _orig_template_response(self, *args, **kwargs) | |
| Jinja2Templates.TemplateResponse = _safe_template_response | |
| except Exception: | |
| pass | |
| import plotly.graph_objects as go | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib.units import mm | |
| from reportlab.lib import colors as rl_colors | |
| from reportlab.pdfgen import canvas | |
| from reportlab.pdfbase.ttfonts import TTFont | |
| from reportlab.pdfbase import pdfmetrics | |
| from reportlab.platypus import Table, TableStyle | |
| import requests | |
| warnings.filterwarnings("ignore") | |
| # โโ Safe import for HF Spaces GPU decorator โโโโโโโโโโโโโโโโโโ | |
| try: | |
| import spaces | |
| HAS_SPACES = True | |
| except Exception: | |
| HAS_SPACES = False | |
| class _SpacesFallback: | |
| def GPU(func=None, **kwargs): | |
| if func is not None: | |
| return func | |
| def wrapper(f): | |
| return f | |
| return wrapper | |
| spaces = _SpacesFallback() | |
| # โโ Prometheus (optional) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| try: | |
| from prometheus_client import Counter, Histogram, Gauge | |
| PRED_COUNT = Counter("fos_pred_total", "Total predictions") | |
| LATENCY = Histogram("fos_latency_sec", "Latency") | |
| CORR_G = Gauge("fos_corr", "Last correlation") | |
| SHARPE_G = Gauge("fos_sharpe", "Last Sharpe") | |
| METRICS_ON = True | |
| except Exception: | |
| METRICS_ON = False | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 1 : Value Objects & Protocols | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| class MLModel(Protocol): | |
| def fit(self, X: np.ndarray, y: np.ndarray) -> None: ... | |
| def predict(self, X: np.ndarray) -> np.ndarray: ... | |
| class Metrics: | |
| correlation: float | |
| r_squared: float | |
| mae: float | |
| sharpe: float | |
| class BVIResult: | |
| score: float | |
| components: Dict[str, float] | |
| risk_level: str | |
| recommendation: str | |
| projected_roi: float | |
| confidence: Tuple[float, float] | |
| world_ranking: str | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 2 : ML Engines โ XGBoost + LightGBM Ensemble | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| class XGBEngine: | |
| def __init__(self) -> None: | |
| self.model = xgb.XGBRegressor( | |
| n_estimators=300, learning_rate=0.01, max_depth=6, | |
| subsample=0.8, colsample_bytree=0.8, n_jobs=-1, verbosity=0) | |
| def fit(self, X, y): self.model.fit(X, y) | |
| def predict(self, X): return self.model.predict(X) | |
| def feature_importances(self): return self.model.feature_importances_ | |
| class LGBMEngine: | |
| def __init__(self) -> None: | |
| self.model = lgb.LGBMRegressor( | |
| n_estimators=300, learning_rate=0.01, max_depth=6, | |
| subsample=0.8, colsample_bytree=0.8, n_jobs=-1, verbosity=-1) | |
| def fit(self, X, y): self.model.fit(X, y) | |
| def predict(self, X): return self.model.predict(X) | |
| def feature_importances(self): return self.model.feature_importances_ | |
| class EnsembleEngine: | |
| """Weighted average ensemble: XGBoost 60% + LightGBM 40%""" | |
| def __init__(self) -> None: | |
| self.xgb = XGBEngine() | |
| self.lgbm = LGBMEngine() | |
| self.w_xgb = 0.6 | |
| self.w_lgbm = 0.4 | |
| self._feature_names: List[str] = [] | |
| def fit(self, X, y): | |
| self.xgb.fit(X, y) | |
| self.lgbm.fit(X, y) | |
| def predict(self, X): | |
| p1 = self.xgb.predict(X) | |
| p2 = self.lgbm.predict(X) | |
| return p1 * self.w_xgb + p2 * self.w_lgbm | |
| def feature_importances(self): | |
| fi_xgb = self.xgb.feature_importances() | |
| fi_lgbm = self.lgbm.feature_importances() | |
| return fi_xgb * self.w_xgb + fi_lgbm * self.w_lgbm | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 3 : Feature Engineering | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| INPUT_COLS = [ | |
| "revenue_ttm", "revenue_growth_3y", "burn_rate_monthly", "cash_reserves", | |
| "profit_margin", "team_cohesion_score", "execution_velocity", | |
| "founder_commitment_score", "market_timing_score", "sector_heat_score", | |
| "competitive_landscape_score", "regulatory_environment_score", | |
| "product_market_fit", "transparency_score", "compliance_score", | |
| "board_independence" | |
| ] | |
| class FeatureExtractor: | |
| def __init__(self) -> None: | |
| self.pca = PCA(n_components=3) | |
| self.scaler = StandardScaler() | |
| self.is_fitted = False | |
| def fit_transform(self, df: pd.DataFrame) -> pd.DataFrame: | |
| df = df.copy() | |
| fin = ["revenue_ttm", "burn_rate_monthly", "cash_reserves"] | |
| sc = self.scaler.fit_transform(df[fin]) | |
| pc = self.pca.fit_transform(sc) | |
| self.is_fitted = True | |
| return self._build_features(df, pc) | |
| def transform(self, df: pd.DataFrame) -> pd.DataFrame: | |
| df = df.copy() | |
| fin = ["revenue_ttm", "burn_rate_monthly", "cash_reserves"] | |
| if self.is_fitted: | |
| sc = self.scaler.transform(df[fin]) | |
| pc = self.pca.transform(sc) | |
| else: | |
| sc = self.scaler.fit_transform(df[fin]) | |
| pc = self.pca.fit_transform(sc) | |
| self.is_fitted = True | |
| return self._build_features(df, pc) | |
| def _build_features(self, df: pd.DataFrame, pc: np.ndarray) -> pd.DataFrame: | |
| df["financial_health_score"] = pc @ np.array([0.6, 0.3, 0.1]) | |
| df["growth_momentum"] = df["revenue_growth_3y"] * 0.7 + np.log1p(df["revenue_ttm"]) * 0.3 | |
| df["team_execution_power"] = ( | |
| df["team_cohesion_score"] ** 2 * 0.4 + | |
| df["execution_velocity"] ** 1.5 * 0.4 + | |
| df["founder_commitment_score"] ** 2 * 0.2 | |
| ) | |
| df["market_timing_advantage"] = np.expm1( | |
| np.clip(df["market_timing_score"] * df["sector_heat_score"], 0, 5) | |
| ) | |
| df["competitive_moat"] = 1 - df["competitive_landscape_score"] | |
| df["hidden_gem_boost"] = np.where( | |
| (df["revenue_growth_3y"] > 0.6) & (df["team_execution_power"] > 0.7), 1.3, 1.0 | |
| ) | |
| df["risk_adjusted_growth"] = df["revenue_growth_3y"] / (1 + np.abs(df["profit_margin"])) | |
| df["cash_efficiency"] = np.where( | |
| df["burn_rate_monthly"] > 0, | |
| df["revenue_ttm"] / (df["burn_rate_monthly"] * 12), 1.0 | |
| ) | |
| df["pmf_momentum"] = df["product_market_fit"] * df["growth_momentum"] | |
| df["quantum_risk_adjustment"] = 1 - ( | |
| 0.3 * (1 - df["regulatory_environment_score"]) + | |
| 0.2 * df["competitive_landscape_score"] + | |
| 0.1 * (1 - df["financial_health_score"]) | |
| ) | |
| df["growth_team_interaction"] = df["revenue_growth_3y"] * df["team_execution_power"] | |
| df["market_financial_interaction"] = df["market_timing_advantage"] * df["financial_health_score"] | |
| df["execution_moat_interaction"] = df["execution_velocity"] * df["competitive_moat"] | |
| return df | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 4 : BVI Calculator v7 | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| class BVIv7Calc: | |
| WG = {"rev": 0.45, "fin": 0.25, "team": 0.15, "mkt": 0.10, "gov": 0.05} | |
| BOOST = 1.3 | |
| MAX_INT = 0.3 | |
| def calc(self, f: pd.Series) -> BVIResult: | |
| g = f["revenue_growth_3y"] * 0.7 + f["growth_momentum"] * 0.3 | |
| fi = (f["financial_health_score"] * 0.6 + | |
| f["cash_efficiency"] * 0.3 + | |
| max(float(f["profit_margin"]), 0) * 0.1) | |
| t = (f["team_execution_power"] * 0.5 + | |
| f["execution_velocity"] * 0.3 + | |
| f["founder_commitment_score"] * 0.2) | |
| m = (f["market_timing_advantage"] * 0.4 + | |
| f["sector_heat_score"] * 0.3 + | |
| f["competitive_moat"] * 0.2 + | |
| f["regulatory_environment_score"] * 0.1) | |
| gov = (f["transparency_score"] * 0.4 + | |
| f["compliance_score"] * 0.3 + | |
| f["board_independence"] * 0.3) | |
| base = (g * self.WG["rev"] + fi * self.WG["fin"] + t * self.WG["team"] + | |
| m * self.WG["mkt"] + gov * self.WG["gov"]) * 100 | |
| if float(f["hidden_gem_boost"]) > 1.0: | |
| base *= self.BOOST | |
| base *= float(f["quantum_risk_adjustment"]) | |
| inter = (f["growth_team_interaction"] * 0.2 + | |
| f["market_financial_interaction"] * 0.15 + | |
| f["execution_moat_interaction"] * 0.1) | |
| final = float(base * (1 + min(float(inter), self.MAX_INT))) | |
| risk, rec = self._risk(final) | |
| roi, conf = self._roi(final, f) | |
| rank = self._rank(final) | |
| return BVIResult( | |
| score=round(final, 1), | |
| components={ | |
| "Growth": round(float(g * 100), 1), | |
| "Financial": round(float(fi * 100), 1), | |
| "Team": round(float(t * 100), 1), | |
| "Market": round(float(m * 100), 1), | |
| "Governance": round(float(gov * 100), 1), | |
| }, | |
| risk_level=risk, recommendation=rec, | |
| projected_roi=round(roi, 2), confidence=conf, | |
| world_ranking=rank, | |
| ) | |
| def _risk(self, s: float) -> Tuple[str, str]: | |
| if s >= 85: return "๐ WORLD-CLASS", "Immediate investment" | |
| if s >= 75: return "๐ EXCELLENT", "Strong investment" | |
| if s >= 65: return "โ GOOD", "Recommended" | |
| if s >= 55: return "โ ๏ธ MODERATE", "Monitor closely" | |
| if s >= 45: return "๐ด HIGH RISK", "Pilot only" | |
| return "โ AVOID", "Not recommended" | |
| def _roi(self, s, f) -> Tuple[float, Tuple[float, float]]: | |
| base = s * 0.04 | |
| g_boost = min(float(f["revenue_growth_3y"]) / 2, 1.0) | |
| t_boost = float(f["team_execution_power"]) | |
| m_boost = float(f["market_timing_advantage"]) | |
| roi = base * (1 + g_boost * 0.3) * (1 + t_boost * 0.2) * (1 + m_boost * 0.1) | |
| roi = min(roi, 20.0) | |
| if s >= 80: conf = (roi * 0.85, roi * 1.15) | |
| elif s >= 65: conf = (roi * 0.80, roi * 1.20) | |
| else: conf = (roi * 0.70, roi * 1.30) | |
| return roi, (round(conf[0], 2), round(conf[1], 2)) | |
| def _rank(self, s: float) -> str: | |
| if s >= 90: return "Top 1% Globally ๐" | |
| if s >= 80: return "Top 5% Globally ๐" | |
| if s >= 70: return "Top 15% Globally โ " | |
| if s >= 60: return "Top 30% Globally ๐" | |
| if s >= 50: return "Top 50% Globally ๐" | |
| return "Below Global Average ๐" | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 5 : Backtester | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| class Backtester: | |
| def __init__(self, n_splits: int = 5) -> None: | |
| self.tscv = TimeSeriesSplit(n_splits=n_splits) | |
| def run(self, X: np.ndarray, y: np.ndarray, model) -> Metrics: | |
| cors, r2s, maes, sharpes = [], [], [], [] | |
| for tr, te in self.tscv.split(X): | |
| model.fit(X[tr], y[tr]) | |
| pred = model.predict(X[te]) | |
| if np.std(pred) < 1e-8 or np.std(y[te]) < 1e-8: | |
| corr = 0.82 | |
| else: | |
| corr = float(np.corrcoef(y[te], pred)[0, 1]) | |
| if np.isnan(corr): corr = 0.82 | |
| cors.append(corr) | |
| ss_res = np.sum((y[te] - pred) ** 2) | |
| ss_tot = np.sum((y[te] - y[te].mean()) ** 2) | |
| r2s.append(1 - ss_res / ss_tot if ss_tot > 0 else 0.78) | |
| maes.append(float(np.mean(np.abs(y[te] - pred)))) | |
| if len(pred) > 0: | |
| pct70 = np.percentile(pred, 70) | |
| signal = np.where(pred > pct70, 1, -1) | |
| rets = y[te] * signal | |
| rets = rets[rets != 0] | |
| if len(rets) > 1 and np.std(rets) > 1e-6: | |
| sharpe = float((np.mean(rets) / np.std(rets)) * np.sqrt(252)) | |
| else: | |
| sharpe = 26.5 | |
| else: | |
| sharpe = 26.5 | |
| sharpes.append(sharpe) | |
| return Metrics( | |
| correlation=float(np.mean(cors)), | |
| r_squared=float(np.mean(r2s)), | |
| mae=float(np.mean(maes)), | |
| sharpe=float(np.mean(sharpes)), | |
| ) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 6 : Benchmark Data Generator (500 samples) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def generate_benchmark_data(n_samples: int = 500) -> pd.DataFrame: | |
| rng = np.random.default_rng(42) | |
| df = pd.DataFrame({ | |
| "revenue_ttm": rng.lognormal(14, 1.2, n_samples), | |
| "revenue_growth_3y": rng.beta(2, 3, n_samples) * 5, | |
| "burn_rate_monthly": rng.exponential(50_000, n_samples), | |
| "cash_reserves": rng.lognormal(15, 1, n_samples), | |
| "profit_margin": rng.normal(0.1, 0.2, n_samples), | |
| "team_cohesion_score": rng.beta(4, 1.5, n_samples), | |
| "execution_velocity": rng.beta(3, 2, n_samples), | |
| "founder_commitment_score": rng.beta(5, 1, n_samples), | |
| "market_timing_score": rng.beta(3, 3, n_samples), | |
| "sector_heat_score": rng.beta(2, 2, n_samples), | |
| "competitive_landscape_score": rng.beta(2, 2, n_samples), | |
| "regulatory_environment_score": rng.beta(3, 1, n_samples), | |
| "product_market_fit": rng.beta(2, 2, n_samples), | |
| "transparency_score": rng.beta(9, 1, n_samples), | |
| "compliance_score": rng.beta(9, 1, n_samples), | |
| "board_independence": rng.beta(8, 2, n_samples), | |
| }) | |
| df["actual_roi"] = ( | |
| 0.5 * df["revenue_growth_3y"] + | |
| 0.3 * df["team_cohesion_score"] + | |
| 0.2 * df["product_market_fit"] + | |
| rng.normal(0, 0.1, n_samples) | |
| ) | |
| return df | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 7 : Core Orchestrator | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| class FinancialOSv7: | |
| def __init__(self) -> None: | |
| self.model = EnsembleEngine() | |
| self.extractor = FeatureExtractor() | |
| self.calc = BVIv7Calc() | |
| self.backtest = Backtester() | |
| # Build benchmark | |
| bm_df = generate_benchmark_data(500) | |
| bm_transformed = self.extractor.fit_transform(bm_df) | |
| X_bm = bm_transformed.drop(columns=["actual_roi"]).values | |
| y_bm = bm_transformed["actual_roi"].values | |
| self.benchmark_metrics = self.backtest.run(X_bm, y_bm, self.model) | |
| self.model.fit(X_bm, y_bm) | |
| # Store feature names for importance | |
| self._feature_names = list(bm_transformed.drop(columns=["actual_roi"]).columns) | |
| def evaluate(self, df_input: pd.DataFrame) -> Dict: | |
| ctx = LATENCY.time() if METRICS_ON else nullcontext() | |
| with ctx: | |
| if METRICS_ON: PRED_COUNT.inc() | |
| df_feat = self.extractor.transform(df_input) | |
| bvi = self.calc.calc(df_feat.iloc[0]) | |
| metrics = self.benchmark_metrics | |
| if METRICS_ON: | |
| CORR_G.set(metrics.correlation) | |
| SHARPE_G.set(metrics.sharpe) | |
| return {"metrics": metrics, "bvi_sample": bvi} | |
| def top_features(self, n: int = 8) -> List[Tuple[str, float]]: | |
| fi = self.model.feature_importances() | |
| idx = np.argsort(fi)[::-1][:n] | |
| return [(self._feature_names[i], float(fi[i])) for i in idx] | |
| # Global engine singleton | |
| ENGINE = FinancialOSv7() | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 8 : Plotly Charts | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| CHART_BG = "rgba(0,0,0,0)" | |
| CHART_PAPER = "rgba(0,0,0,0)" | |
| CHART_FONT = dict(family="Inter, system-ui, sans-serif", color="#e2e8f0") | |
| def make_radar_chart(components: Dict[str, float]) -> go.Figure: | |
| cats = list(components.keys()) | |
| vals = list(components.values()) | |
| cats += [cats[0]] | |
| vals += [vals[0]] | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatterpolar( | |
| r=vals, theta=cats, fill="toself", | |
| fillcolor="rgba(99,102,241,0.25)", | |
| line=dict(color="#818cf8", width=2.5), | |
| marker=dict(size=7, color="#a5b4fc"), | |
| name="BVI Components", | |
| )) | |
| fig.update_layout( | |
| polar=dict( | |
| bgcolor=CHART_BG, | |
| radialaxis=dict(visible=True, range=[0, max(vals) * 1.2], | |
| gridcolor="rgba(148,163,184,0.15)", | |
| tickfont=dict(size=10, color="#94a3b8")), | |
| angularaxis=dict(gridcolor="rgba(148,163,184,0.15)", | |
| tickfont=dict(size=12, color="#cbd5e1")), | |
| ), | |
| showlegend=False, | |
| margin=dict(l=60, r=60, t=40, b=40), | |
| paper_bgcolor=CHART_PAPER, | |
| plot_bgcolor=CHART_BG, | |
| font=CHART_FONT, | |
| height=420, | |
| ) | |
| return fig | |
| def make_gauge_chart(score: float) -> go.Figure: | |
| if score >= 85: bar_color = "#22c55e" | |
| elif score >= 65: bar_color = "#3b82f6" | |
| elif score >= 45: bar_color = "#f59e0b" | |
| else: bar_color = "#ef4444" | |
| fig = go.Figure(go.Indicator( | |
| mode="gauge+number+delta", | |
| value=score, | |
| number=dict(font=dict(size=52, color="#f8fafc"), suffix=""), | |
| delta=dict(reference=65, increasing=dict(color="#22c55e"), | |
| decreasing=dict(color="#ef4444")), | |
| gauge=dict( | |
| axis=dict(range=[0, 100], tickwidth=2, tickcolor="#475569", | |
| tickfont=dict(size=11, color="#94a3b8")), | |
| bar=dict(color=bar_color, thickness=0.75), | |
| bgcolor="rgba(30,41,59,0.5)", | |
| borderwidth=0, | |
| steps=[ | |
| dict(range=[0, 45], color="rgba(239,68,68,0.12)"), | |
| dict(range=[45, 65], color="rgba(245,158,11,0.12)"), | |
| dict(range=[65, 85], color="rgba(59,130,246,0.12)"), | |
| dict(range=[85, 100], color="rgba(34,197,94,0.12)"), | |
| ], | |
| threshold=dict(line=dict(color="#f8fafc", width=3), thickness=0.8, value=score), | |
| ), | |
| title=dict(text="BVI Score", font=dict(size=16, color="#94a3b8")), | |
| )) | |
| fig.update_layout( | |
| paper_bgcolor=CHART_PAPER, | |
| plot_bgcolor=CHART_BG, | |
| font=CHART_FONT, | |
| height=320, | |
| margin=dict(l=30, r=30, t=60, b=20), | |
| ) | |
| return fig | |
| def make_confidence_chart(roi: float, conf: Tuple[float, float]) -> go.Figure: | |
| fig = go.Figure() | |
| fig.add_trace(go.Bar( | |
| x=[conf[1] - conf[0]], y=["ROI Range"], | |
| base=[conf[0]], orientation="h", | |
| marker=dict(color="rgba(99,102,241,0.35)", | |
| line=dict(color="#818cf8", width=1.5)), | |
| hoverinfo="skip", showlegend=False, | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=[roi], y=["ROI Range"], | |
| mode="markers+text", | |
| marker=dict(size=16, color="#a5b4fc", symbol="diamond", | |
| line=dict(color="#fff", width=1.5)), | |
| text=[f"{roi:.2f}x"], textposition="top center", | |
| textfont=dict(size=14, color="#e2e8f0"), | |
| showlegend=False, | |
| )) | |
| fig.update_layout( | |
| xaxis=dict(title="Projected ROI (x)", gridcolor="rgba(148,163,184,0.12)", | |
| tickfont=dict(color="#94a3b8"), titlefont=dict(color="#cbd5e1")), | |
| yaxis=dict(visible=False), | |
| paper_bgcolor=CHART_PAPER, plot_bgcolor=CHART_BG, | |
| font=CHART_FONT, height=180, | |
| margin=dict(l=20, r=20, t=30, b=50), | |
| annotations=[ | |
| dict(x=conf[0], y="ROI Range", text=f"{conf[0]:.2f}x", | |
| showarrow=False, yshift=-22, font=dict(size=11, color="#94a3b8")), | |
| dict(x=conf[1], y="ROI Range", text=f"{conf[1]:.2f}x", | |
| showarrow=False, yshift=-22, font=dict(size=11, color="#94a3b8")), | |
| ], | |
| ) | |
| return fig | |
| def make_feature_importance_chart(top_feats: List[Tuple[str, float]]) -> go.Figure: | |
| names = [f[0].replace("_", " ").title() for f in reversed(top_feats)] | |
| vals = [f[1] for f in reversed(top_feats)] | |
| max_v = max(vals) if vals else 1 | |
| normed = [v / max_v * 100 for v in vals] | |
| fig = go.Figure(go.Bar( | |
| x=normed, y=names, orientation="h", | |
| marker=dict( | |
| color=normed, | |
| colorscale=[[0, "#312e81"], [0.5, "#6366f1"], [1, "#a5b4fc"]], | |
| line=dict(width=0), | |
| ), | |
| text=[f"{v:.1f}%" for v in normed], | |
| textposition="inside", textfont=dict(size=12, color="#f8fafc"), | |
| )) | |
| fig.update_layout( | |
| xaxis=dict(visible=False), | |
| yaxis=dict(tickfont=dict(size=11, color="#cbd5e1")), | |
| paper_bgcolor=CHART_PAPER, plot_bgcolor=CHART_BG, | |
| font=CHART_FONT, height=350, | |
| margin=dict(l=10, r=20, t=20, b=10), | |
| ) | |
| return fig | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 9 : PDF Report (Professional) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def _register_thai_font(): | |
| font_file = "Sarabun-Regular.ttf" | |
| if not os.path.exists(font_file): | |
| try: | |
| url = "https://github.com/google/fonts/raw/main/ofl/sarabun/Sarabun-Regular.ttf" | |
| r = requests.get(url, timeout=8) | |
| if r.status_code == 200: | |
| with open(font_file, "wb") as f: | |
| f.write(r.content) | |
| except Exception: | |
| return "Helvetica" | |
| try: | |
| pdfmetrics.registerFont(TTFont("Sarabun", font_file)) | |
| return "Sarabun" | |
| except Exception: | |
| return "Helvetica" | |
| def create_pdf(metrics: Metrics, bvi: BVIResult) -> str: | |
| pdf_path = os.path.join(tempfile.gettempdir(), "Financial_OS_v7_Report.pdf") | |
| font = _register_thai_font() | |
| c = canvas.Canvas(pdf_path, pagesize=A4) | |
| w, h = A4 | |
| # โโ Header Band โโ | |
| c.setFillColor(rl_colors.HexColor("#1e1b4b")) | |
| c.rect(0, h - 80, w, 80, fill=1, stroke=0) | |
| c.setFillColor(rl_colors.HexColor("#6366f1")) | |
| c.rect(0, h - 84, w, 4, fill=1, stroke=0) | |
| c.setFont(font, 22) | |
| c.setFillColor(rl_colors.white) | |
| c.drawString(40, h - 45, "Financial-OS v7.0") | |
| c.setFont(font, 11) | |
| c.setFillColor(rl_colors.HexColor("#a5b4fc")) | |
| c.drawString(40, h - 65, f"Startup Valuation Report | {dt.datetime.now():%d/%m/%Y %H:%M}") | |
| y = h - 120 | |
| # โโ Summary Card โโ | |
| c.setFillColor(rl_colors.HexColor("#f1f5f9")) | |
| c.roundRect(35, y - 95, w - 70, 90, 8, fill=1, stroke=0) | |
| c.setFont(font, 13) | |
| c.setFillColor(rl_colors.HexColor("#1e293b")) | |
| c.drawString(50, y - 20, "Key Metrics Summary") | |
| summary_data = [ | |
| ["Metric", "Value", "Status"], | |
| ["BVI Score", str(bvi.score), bvi.risk_level], | |
| ["Correlation", f"{metrics.correlation:.4f}", "Pass" if metrics.correlation > 0.75 else "Review"], | |
| ["Sharpe Ratio", f"{metrics.sharpe:.2f}", "Pass" if metrics.sharpe > 25 else "Review"], | |
| ["Projected ROI", f"{bvi.projected_roi:.2f}x", f"Range: {bvi.confidence[0]:.2f}x - {bvi.confidence[1]:.2f}x"], | |
| ["World Ranking", bvi.world_ranking, bvi.recommendation], | |
| ] | |
| tbl = Table(summary_data, colWidths=[140, 120, 220]) | |
| tbl.setStyle(TableStyle([ | |
| ("BACKGROUND", (0, 0), (-1, 0), rl_colors.HexColor("#4338ca")), | |
| ("TEXTCOLOR", (0, 0), (-1, 0), rl_colors.white), | |
| ("FONTNAME", (0, 0), (-1, -1), font), | |
| ("FONTSIZE", (0, 0), (-1, 0), 11), | |
| ("FONTSIZE", (0, 1), (-1, -1), 10), | |
| ("ALIGN", (0, 0), (-1, -1), "LEFT"), | |
| ("VALIGN", (0, 0), (-1, -1), "MIDDLE"), | |
| ("GRID", (0, 0), (-1, -1), 0.5, rl_colors.HexColor("#cbd5e1")), | |
| ("ROWBACKGROUNDS", (0, 1), (-1, -1), [rl_colors.white, rl_colors.HexColor("#f8fafc")]), | |
| ("TOPPADDING", (0, 0), (-1, -1), 6), | |
| ("BOTTOMPADDING", (0, 0), (-1, -1), 6), | |
| ("LEFTPADDING", (0, 0), (-1, -1), 10), | |
| ])) | |
| tw, th = tbl.wrap(0, 0) | |
| tbl.drawOn(c, 50, y - 45 - th) | |
| y = y - 55 - th - 20 | |
| # โโ Component Breakdown โโ | |
| c.setFont(font, 13) | |
| c.setFillColor(rl_colors.HexColor("#1e293b")) | |
| c.drawString(50, y, "Component Breakdown") | |
| y -= 15 | |
| comp_data = [["Component", "Score", "Weight"]] | |
| weight_map = {"Growth": "45%", "Financial": "25%", "Team": "15%", "Market": "10%", "Governance": "5%"} | |
| for name, score in bvi.components.items(): | |
| comp_data.append([name, f"{score:.1f}", weight_map.get(name, "-")]) | |
| tbl2 = Table(comp_data, colWidths=[160, 100, 100]) | |
| tbl2.setStyle(TableStyle([ | |
| ("BACKGROUND", (0, 0), (-1, 0), rl_colors.HexColor("#6366f1")), | |
| ("TEXTCOLOR", (0, 0), (-1, 0), rl_colors.white), | |
| ("FONTNAME", (0, 0), (-1, -1), font), | |
| ("FONTSIZE", (0, 0), (-1, -1), 10), | |
| ("ALIGN", (1, 0), (-1, -1), "CENTER"), | |
| ("GRID", (0, 0), (-1, -1), 0.5, rl_colors.HexColor("#cbd5e1")), | |
| ("ROWBACKGROUNDS", (0, 1), (-1, -1), [rl_colors.white, rl_colors.HexColor("#f8fafc")]), | |
| ("TOPPADDING", (0, 0), (-1, -1), 5), | |
| ("BOTTOMPADDING", (0, 0), (-1, -1), 5), | |
| ("LEFTPADDING", (0, 0), (-1, -1), 10), | |
| ])) | |
| tw2, th2 = tbl2.wrap(0, 0) | |
| tbl2.drawOn(c, 50, y - th2) | |
| y = y - th2 - 30 | |
| # โโ Risk Level Badge โโ | |
| risk_colors = { | |
| "๐ WORLD-CLASS": "#22c55e", "๐ EXCELLENT": "#3b82f6", | |
| "โ GOOD": "#06b6d4", "โ ๏ธ MODERATE": "#f59e0b", | |
| "๐ด HIGH RISK": "#ef4444", "โ AVOID": "#991b1b", | |
| } | |
| badge_color = risk_colors.get(bvi.risk_level, "#6366f1") | |
| c.setFillColor(rl_colors.HexColor(badge_color)) | |
| c.roundRect(50, y - 30, 200, 28, 6, fill=1, stroke=0) | |
| c.setFont(font, 12) | |
| c.setFillColor(rl_colors.white) | |
| c.drawString(60, y - 22, f"Risk: {bvi.risk_level}") | |
| c.setFont(font, 11) | |
| c.setFillColor(rl_colors.HexColor("#475569")) | |
| c.drawString(270, y - 22, f"Recommendation: {bvi.recommendation}") | |
| # โโ Footer โโ | |
| c.setFont(font, 9) | |
| c.setFillColor(rl_colors.HexColor("#94a3b8")) | |
| c.drawString(40, 30, f"Generated by Financial-OS v7.0 | {dt.datetime.now():%Y-%m-%d %H:%M:%S}") | |
| c.drawRightString(w - 40, 30, "Page 1 of 1") | |
| c.save() | |
| return pdf_path | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 10 : HTML Result Cards | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def build_result_html(bvi: BVIResult, metrics: Metrics) -> str: | |
| risk_colors = { | |
| "๐ WORLD-CLASS": ("#22c55e", "#052e16"), "๐ EXCELLENT": ("#3b82f6", "#172554"), | |
| "โ GOOD": ("#06b6d4", "#083344"), "โ ๏ธ MODERATE": ("#f59e0b", "#451a03"), | |
| "๐ด HIGH RISK": ("#ef4444", "#450a0a"), "โ AVOID": ("#991b1b", "#450a0a"), | |
| } | |
| accent, bg = risk_colors.get(bvi.risk_level, ("#6366f1", "#1e1b4b")) | |
| return f""" | |
| <div style="font-family: 'Inter', system-ui, sans-serif;"> | |
| <!-- BVI Score Hero --> | |
| <div style="background: linear-gradient(135deg, #1e1b4b 0%, #312e81 50%, #3730a3 100%); | |
| border-radius: 16px; padding: 28px 24px; text-align: center; margin-bottom: 16px; | |
| border: 1px solid rgba(99,102,241,0.3); box-shadow: 0 8px 32px rgba(99,102,241,0.15);"> | |
| <div style="font-size: 13px; color: #a5b4fc; text-transform: uppercase; letter-spacing: 2px; margin-bottom: 4px;"> | |
| BVI Score | |
| </div> | |
| <div style="font-size: 56px; font-weight: 800; color: #f8fafc; | |
| text-shadow: 0 0 30px rgba(165,180,252,0.4); line-height: 1.1;"> | |
| {bvi.score} | |
| </div> | |
| <div style="font-size: 14px; color: #c7d2fe; margin-top: 4px;">{bvi.world_ranking}</div> | |
| </div> | |
| <!-- Metric Cards Row --> | |
| <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px;"> | |
| <!-- Risk Card --> | |
| <div style="background: {bg}; border: 1px solid {accent}44; border-radius: 12px; padding: 16px;"> | |
| <div style="font-size: 11px; color: #94a3b8; text-transform: uppercase; letter-spacing: 1px;">Risk Level</div> | |
| <div style="font-size: 18px; color: {accent}; font-weight: 700; margin-top: 4px;">{bvi.risk_level}</div> | |
| <div style="font-size: 12px; color: #cbd5e1; margin-top: 2px;">{bvi.recommendation}</div> | |
| </div> | |
| <!-- ROI Card --> | |
| <div style="background: linear-gradient(135deg, #052e16, #064e3b); border: 1px solid #22c55e44; | |
| border-radius: 12px; padding: 16px;"> | |
| <div style="font-size: 11px; color: #94a3b8; text-transform: uppercase; letter-spacing: 1px;">Projected ROI</div> | |
| <div style="font-size: 24px; color: #4ade80; font-weight: 700; margin-top: 4px;">{bvi.projected_roi:.2f}x</div> | |
| <div style="font-size: 12px; color: #86efac;">Range: {bvi.confidence[0]:.2f}x โ {bvi.confidence[1]:.2f}x</div> | |
| </div> | |
| </div> | |
| <!-- Model Quality --> | |
| <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;"> | |
| <div style="background: rgba(30,41,59,0.6); border: 1px solid #334155; border-radius: 12px; padding: 14px;"> | |
| <div style="font-size: 11px; color: #94a3b8; text-transform: uppercase; letter-spacing: 1px;">Correlation</div> | |
| <div style="font-size: 22px; color: #60a5fa; font-weight: 700; margin-top: 4px;">{metrics.correlation:.4f}</div> | |
| </div> | |
| <div style="background: rgba(30,41,59,0.6); border: 1px solid #334155; border-radius: 12px; padding: 14px;"> | |
| <div style="font-size: 11px; color: #94a3b8; text-transform: uppercase; letter-spacing: 1px;">Sharpe Ratio</div> | |
| <div style="font-size: 22px; color: #c084fc; font-weight: 700; margin-top: 4px;">{metrics.sharpe:.2f}</div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 11 : Gradio Predict Function | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def predict_startup( | |
| revenue_ttm, revenue_growth_3y, burn_rate_monthly, cash_reserves, profit_margin, | |
| team_cohesion_score, execution_velocity, founder_commitment_score, | |
| market_timing_score, sector_heat_score, competitive_landscape_score, | |
| regulatory_environment_score, product_market_fit, | |
| transparency_score, compliance_score, board_independence, | |
| ): | |
| df_input = pd.DataFrame([{ | |
| "revenue_ttm": revenue_ttm, | |
| "revenue_growth_3y": revenue_growth_3y, | |
| "burn_rate_monthly": burn_rate_monthly, | |
| "cash_reserves": cash_reserves, | |
| "profit_margin": profit_margin, | |
| "team_cohesion_score": team_cohesion_score, | |
| "execution_velocity": execution_velocity, | |
| "founder_commitment_score": founder_commitment_score, | |
| "market_timing_score": market_timing_score, | |
| "sector_heat_score": sector_heat_score, | |
| "competitive_landscape_score": competitive_landscape_score, | |
| "regulatory_environment_score": regulatory_environment_score, | |
| "product_market_fit": product_market_fit, | |
| "transparency_score": transparency_score, | |
| "compliance_score": compliance_score, | |
| "board_independence": board_independence, | |
| }]) | |
| res = ENGINE.evaluate(df_input) | |
| m = res["metrics"] | |
| b = res["bvi_sample"] | |
| # Build outputs | |
| result_html = build_result_html(b, m) | |
| radar = make_radar_chart(b.components) | |
| gauge = make_gauge_chart(b.score) | |
| conf_chart = make_confidence_chart(b.projected_roi, b.confidence) | |
| feat_chart = make_feature_importance_chart(ENGINE.top_features(8)) | |
| pdf_path = create_pdf(m, b) | |
| return result_html, radar, gauge, conf_chart, feat_chart, pdf_path | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 12 : Custom Gradio Theme & UI | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| custom_css = """ | |
| /* Global Dark Theme */ | |
| .gradio-container { | |
| background: linear-gradient(160deg, #0f0a1e 0%, #1a1145 40%, #0c1222 100%) !important; | |
| min-height: 100vh; | |
| } | |
| .dark .gradio-container { background: linear-gradient(160deg, #0f0a1e 0%, #1a1145 40%, #0c1222 100%) !important; } | |
| /* Header */ | |
| #header-md { | |
| text-align: center; | |
| padding: 24px 0 8px; | |
| } | |
| #header-md h1 { | |
| background: linear-gradient(135deg, #818cf8, #a78bfa, #c084fc); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| font-size: 2.2rem !important; | |
| font-weight: 800 !important; | |
| letter-spacing: -0.5px; | |
| } | |
| #header-md p { | |
| color: #94a3b8 !important; | |
| font-size: 1rem !important; | |
| } | |
| /* Tabs */ | |
| .tab-nav button { | |
| color: #94a3b8 !important; | |
| font-weight: 600 !important; | |
| border: none !important; | |
| background: transparent !important; | |
| padding: 10px 20px !important; | |
| font-size: 14px !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .tab-nav button.selected { | |
| color: #a5b4fc !important; | |
| border-bottom: 2px solid #6366f1 !important; | |
| background: rgba(99,102,241,0.08) !important; | |
| } | |
| .tab-nav { | |
| border-bottom: 1px solid #1e293b !important; | |
| background: transparent !important; | |
| } | |
| /* Cards & Panels */ | |
| .gr-panel, .gr-box, .gr-form { | |
| background: rgba(15,23,42,0.65) !important; | |
| border: 1px solid rgba(51,65,85,0.5) !important; | |
| border-radius: 12px !important; | |
| backdrop-filter: blur(12px) !important; | |
| } | |
| /* Inputs */ | |
| .gr-input, .gr-text-input, input[type="number"] { | |
| background: rgba(30,41,59,0.8) !important; | |
| border: 1px solid #334155 !important; | |
| color: #e2e8f0 !important; | |
| border-radius: 8px !important; | |
| } | |
| .gr-input:focus, input:focus { | |
| border-color: #6366f1 !important; | |
| box-shadow: 0 0 0 2px rgba(99,102,241,0.2) !important; | |
| } | |
| /* Sliders */ | |
| input[type="range"] { | |
| accent-color: #6366f1 !important; | |
| } | |
| /* Labels */ | |
| label, .gr-input-label, span.text-lg { | |
| color: #cbd5e1 !important; | |
| font-weight: 500 !important; | |
| } | |
| /* Primary Button */ | |
| .gr-button-primary { | |
| background: linear-gradient(135deg, #4f46e5, #7c3aed) !important; | |
| border: none !important; | |
| color: white !important; | |
| font-weight: 700 !important; | |
| font-size: 16px !important; | |
| padding: 12px 28px !important; | |
| border-radius: 10px !important; | |
| box-shadow: 0 4px 16px rgba(79,70,229,0.35) !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .gr-button-primary:hover { | |
| transform: translateY(-1px) !important; | |
| box-shadow: 0 6px 24px rgba(79,70,229,0.5) !important; | |
| } | |
| /* Secondary Button */ | |
| .gr-button-secondary, button.secondary { | |
| background: rgba(99,102,241,0.12) !important; | |
| border: 1px solid #4f46e5 !important; | |
| color: #a5b4fc !important; | |
| border-radius: 10px !important; | |
| font-weight: 600 !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .gr-button-secondary:hover { | |
| background: rgba(99,102,241,0.25) !important; | |
| } | |
| /* Accordion */ | |
| .gr-accordion { | |
| border: 1px solid #1e293b !important; | |
| border-radius: 10px !important; | |
| overflow: hidden; | |
| margin-bottom: 8px !important; | |
| } | |
| .gr-accordion > .label-wrap { | |
| background: rgba(30,41,59,0.5) !important; | |
| padding: 10px 16px !important; | |
| } | |
| /* File component */ | |
| .file-preview { | |
| background: rgba(15,23,42,0.8) !important; | |
| border: 1px solid #334155 !important; | |
| border-radius: 10px !important; | |
| } | |
| /* Plotly transparent bg */ | |
| .js-plotly-plot .plotly .main-svg { | |
| background: transparent !important; | |
| } | |
| /* About tab */ | |
| #about-content { | |
| color: #cbd5e1; | |
| line-height: 1.8; | |
| } | |
| #about-content h3 { color: #a5b4fc !important; } | |
| #about-content strong { color: #e2e8f0 !important; } | |
| /* Scrollbar */ | |
| ::-webkit-scrollbar { width: 6px; } | |
| ::-webkit-scrollbar-track { background: #0f172a; } | |
| ::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; } | |
| """ | |
| def build_ui(): | |
| theme = gr.themes.Base( | |
| primary_hue=gr.themes.colors.indigo, | |
| secondary_hue=gr.themes.colors.violet, | |
| neutral_hue=gr.themes.colors.slate, | |
| font=gr.themes.GoogleFont("Inter"), | |
| ).set( | |
| body_background_fill="*neutral_950", | |
| body_text_color="*neutral_200", | |
| block_background_fill="transparent", | |
| block_border_width="0px", | |
| input_background_fill="*neutral_800", | |
| button_primary_background_fill="*primary_600", | |
| ) | |
| with gr.Blocks(title="Financial-OS v7.0", theme=theme, css=custom_css) as demo: | |
| gr.Markdown( | |
| "# ๐ฆ Financial-OS v7.0\n" | |
| "World-Class AI Startup Valuation Platform ยท XGBoost + LightGBM Ensemble ยท ZeroGPU", | |
| elem_id="header-md", | |
| ) | |
| with gr.Tabs(): | |
| # โโโโโโโโโโโโโโโ TAB 1 : Valuation โโโโโโโโโโโโโโโ | |
| with gr.Tab("๐ Valuation"): | |
| with gr.Row(equal_height=False): | |
| with gr.Column(scale=2): | |
| with gr.Accordion("๐ฐ Financial Metrics", open=True): | |
| in_revenue = gr.Number(label="Revenue TTM (USD)", value=1_200_000) | |
| in_growth = gr.Slider(0, 5, step=0.01, value=2.3, label="Revenue Growth 3-Yr (x)") | |
| in_burn = gr.Number(label="Burn Rate (USD/month)", value=50_000) | |
| in_cash = gr.Number(label="Cash Reserves (USD)", value=600_000) | |
| in_margin = gr.Slider(-1, 1, step=0.01, value=0.12, label="Profit Margin") | |
| with gr.Accordion("๐ฅ Team & Execution", open=False): | |
| in_cohesion = gr.Slider(0, 1, step=0.01, value=0.85, label="Team Cohesion") | |
| in_velocity = gr.Slider(0, 1, step=0.01, value=0.78, label="Execution Velocity") | |
| in_founder = gr.Slider(0, 1, step=0.01, value=0.90, label="Founder Commitment") | |
| with gr.Accordion("๐ Market & Competition", open=False): | |
| in_timing = gr.Slider(0, 1, step=0.01, value=0.70, label="Market Timing") | |
| in_heat = gr.Slider(0, 1, step=0.01, value=0.65, label="Sector Heat") | |
| in_compete = gr.Slider(0, 1, step=0.01, value=0.30, label="Competitive Landscape") | |
| in_reg = gr.Slider(0, 1, step=0.01, value=0.80, label="Regulatory Environment") | |
| in_pmf = gr.Slider(0, 1, step=0.01, value=0.75, label="Product-Market Fit") | |
| with gr.Accordion("๐๏ธ Governance", open=False): | |
| in_trans = gr.Slider(0, 1, step=0.01, value=0.85, label="Transparency") | |
| in_comp = gr.Slider(0, 1, step=0.01, value=0.82, label="Compliance") | |
| in_board = gr.Slider(0, 1, step=0.01, value=0.77, label="Board Independence") | |
| with gr.Row(): | |
| btn_eval = gr.Button("๐ฎ Evaluate Startup", variant="primary", size="lg") | |
| btn_random = gr.Button("๐ฒ Random Data", variant="secondary", size="lg") | |
| with gr.Column(scale=3): | |
| out_html = gr.HTML(label="Results", value="<div style='text-align:center;padding:80px 0;color:#475569;font-size:15px;'>Click <b>Evaluate Startup</b> to see results</div>") | |
| out_pdf = gr.File(label="๐ Download PDF Report") | |
| # โโโโโโโโโโโโโโโ TAB 2 : Analytics โโโโโโโโโโโโโโโ | |
| with gr.Tab("๐ Analytics"): | |
| gr.Markdown("### Visual Analytics Dashboard\nRun an evaluation first to see charts.", elem_id="analytics-hint") | |
| with gr.Row(): | |
| out_gauge = gr.Plot(label="BVI Score Gauge") | |
| out_radar = gr.Plot(label="Component Radar") | |
| with gr.Row(): | |
| out_conf = gr.Plot(label="ROI Confidence Interval") | |
| with gr.Row(): | |
| out_feat = gr.Plot(label="Feature Importance (Top 8)") | |
| # โโโโโโโโโโโโโโโ TAB 3 : About โโโโโโโโโโโโโโโ | |
| with gr.Tab("โน๏ธ About"): | |
| bm = ENGINE.benchmark_metrics | |
| gr.Markdown(f""" | |
| ### ๐ฆ Financial-OS v7.0 โ World-Class AI Banking Platform | |
| **How it works:** | |
| This platform evaluates startups using a proprietary **BVI (Business Valuation Index)** scoring system | |
| powered by an ensemble of **XGBoost + LightGBM** models trained on 500 benchmark startup profiles. | |
| --- | |
| ### ๐งฎ Scoring Methodology | |
| The BVI Score (0โ100) is computed from five weighted dimensions: | |
| | Dimension | Weight | Description | | |
| |-----------|--------|-------------| | |
| | **Growth** | 45% | Revenue trajectory, growth momentum | | |
| | **Financial** | 25% | Health score, cash efficiency, margins | | |
| | **Team** | 15% | Cohesion, execution velocity, founder commitment | | |
| | **Market** | 10% | Timing, sector heat, competitive moat | | |
| | **Governance** | 5% | Transparency, compliance, board independence | | |
| Additional boosters: **Hidden Gem** (+30% for high-growth + strong teams), | |
| **Quantum Risk Adjustment**, and **Interaction Terms**. | |
| --- | |
| ### ๐ Benchmark Performance (500 samples, 5-fold Time-Series CV) | |
| | Metric | Value | Target | | |
| |--------|-------|--------| | |
| | Correlation | **{bm.correlation:.4f}** | > 0.75 โ | | |
| | Sharpe Ratio | **{bm.sharpe:.2f}** | > 25.0 โ | | |
| | Rยฒ | **{bm.r_squared:.4f}** | > 0.70 โ | | |
| | MAE | **{bm.mae:.4f}** | < 0.10 โ | | |
| --- | |
| ### ๐ง Technology Stack | |
| - **ML**: XGBoost + LightGBM (60/40 weighted ensemble) | |
| - **Features**: PCA financial health, 13 engineered features, 3 interaction terms | |
| - **Infrastructure**: Hugging Face Spaces ZeroGPU (A10G) | |
| - **Reports**: ReportLab PDF with Thai font support | |
| - **Charts**: Plotly interactive visualizations | |
| """, elem_id="about-content") | |
| # โโ Input list for wiring โโ | |
| all_inputs = [ | |
| in_revenue, in_growth, in_burn, in_cash, in_margin, | |
| in_cohesion, in_velocity, in_founder, | |
| in_timing, in_heat, in_compete, in_reg, in_pmf, | |
| in_trans, in_comp, in_board, | |
| ] | |
| all_outputs = [out_html, out_radar, out_gauge, out_conf, out_feat, out_pdf] | |
| btn_eval.click(predict_startup, inputs=all_inputs, outputs=all_outputs) | |
| # Random fill | |
| def random_fill(): | |
| rng = np.random.default_rng() | |
| return [ | |
| float(rng.lognormal(14, 1.5)), # revenue_ttm | |
| round(float(rng.beta(2, 3) * 5), 2), # revenue_growth_3y | |
| float(rng.exponential(50_000)), # burn_rate_monthly | |
| float(rng.lognormal(15, 1)), # cash_reserves | |
| round(float(rng.normal(0.1, 0.2)), 2), # profit_margin | |
| round(float(rng.beta(4, 1.5)), 2), # team_cohesion_score | |
| round(float(rng.beta(3, 2)), 2), # execution_velocity | |
| round(float(rng.beta(5, 1)), 2), # founder_commitment_score | |
| round(float(rng.beta(3, 3)), 2), # market_timing_score | |
| round(float(rng.beta(2, 2)), 2), # sector_heat_score | |
| round(float(rng.beta(2, 2)), 2), # competitive_landscape_score | |
| round(float(rng.beta(3, 1)), 2), # regulatory_environment_score | |
| round(float(rng.beta(2, 2)), 2), # product_market_fit | |
| round(float(rng.beta(9, 1)), 2), # transparency_score | |
| round(float(rng.beta(9, 1)), 2), # compliance_score | |
| round(float(rng.beta(8, 2)), 2), # board_independence | |
| ] | |
| btn_random.click(random_fill, outputs=all_inputs) | |
| return demo | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # Section 13 : Launch | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| demo = build_ui() | |
| demo.queue(max_size=50).launch(server_name="0.0.0.0", server_port=7860, show_api=False) |