# ═══════════════════════════════════════════════════════════════ # 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: @staticmethod 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: ... @dataclass class Metrics: correlation: float r_squared: float mae: float sharpe: float @dataclass 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"""