Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import numpy as np | |
| from datetime import datetime | |
| import plotly.graph_objects as go | |
| import re | |
| from urllib.parse import urlparse | |
| import requests | |
| import json | |
| import os | |
| class StartupValuationCalculator: | |
| def __init__(self): | |
| # ์ ์ข ๋ณ ๋ฒค์น๋งํฌ ๋ฉํฐํ (EV/ARR) | |
| self.industry_multiples = { | |
| "SaaS - B2B": {"low": 3, "mid": 6, "high": 10}, | |
| "SaaS - B2C": {"low": 2, "mid": 4, "high": 7}, | |
| "๋ง์ผํ๋ ์ด์ค": {"low": 2, "mid": 5, "high": 8}, | |
| "์ด์ปค๋จธ์ค": {"low": 1, "mid": 2.5, "high": 4}, | |
| "ํํ ํฌ": {"low": 3, "mid": 5, "high": 8}, | |
| "ํฌ์ค์ผ์ด": {"low": 4, "mid": 7, "high": 12}, | |
| "AI/๋ฅํ ํฌ": {"low": 5, "mid": 8, "high": 15}, | |
| "๊ธฐํ": {"low": 2, "mid": 4, "high": 6} | |
| } | |
| # ์ฑ์ฅ๋ฅ ์กฐ์ ๊ณ์ | |
| self.growth_adjustments = { | |
| "0-20%": 0.7, | |
| "20-50%": 0.9, | |
| "50-100%": 1.1, | |
| "100-200%": 1.3, | |
| "200%+": 1.5 | |
| } | |
| # ๋ฒํฌ์ค ๋ฐฉ๋ฒ ์นดํ ๊ณ ๋ฆฌ๋ณ ์ต๋๊ฐ ($500K each) | |
| self.berkus_max_values = { | |
| "sound_idea": 500000, | |
| "prototype": 500000, | |
| "quality_team": 500000, | |
| "strategic_relationships": 500000, | |
| "product_rollout": 500000 | |
| } | |
| # ์ค์ฝ์ด์นด๋ ๊ฐ์ค์น | |
| self.scorecard_weights = { | |
| "team": 0.30, | |
| "market_size": 0.25, | |
| "product": 0.15, | |
| "competition": 0.10, | |
| "marketing": 0.10, | |
| "need_for_funding": 0.05, | |
| "other": 0.05 | |
| } | |
| # ์ธ์ด๋ณ ํ ์คํธ | |
| self.translations = { | |
| "ko": { | |
| "title": "๐ฆ ์คํํธ์ ๊ฐ์นํ๊ฐ ์๋ํ ์์คํ v3.0", | |
| "subtitle": "๋ฒํฌ์ค ๋ฐฉ๋ฒ๊ณผ ์ค์ฝ์ด์นด๋ ๋ฐฉ๋ฒ์ ํฌํจํ ์ข ํฉ ํ๊ฐ", | |
| "valuation_result": "๊ฐ์นํ๊ฐ ๊ฒฐ๊ณผ", | |
| "company_value": "๊ธฐ์ ๊ฐ์น", | |
| "arr": "์ฐ๊ฐ ๋ฐ๋ณต ๋งค์ถ", | |
| "multiple": "์ ์ฉ ๋ฉํฐํ", | |
| "unit_economics": "๋จ์๊ฒฝ์ ", | |
| "berkus_score": "๋ฒํฌ์ค ํ๊ฐ", | |
| "scorecard_score": "์ค์ฝ์ด์นด๋ ํ๊ฐ", | |
| "financial_health": "์ฌ๋ฌด ๊ฑด์ ์ฑ", | |
| "insights": "ํ๊ฐ ์ธ์ฌ์ดํธ" | |
| }, | |
| "en": { | |
| "title": "๐ฆ Startup Valuation System v3.0", | |
| "subtitle": "Comprehensive valuation with Berkus and Scorecard methods", | |
| "valuation_result": "Valuation Result", | |
| "company_value": "Company Value", | |
| "arr": "Annual Recurring Revenue", | |
| "multiple": "Applied Multiple", | |
| "unit_economics": "Unit Economics", | |
| "berkus_score": "Berkus Score", | |
| "scorecard_score": "Scorecard Score", | |
| "financial_health": "Financial Health", | |
| "insights": "Valuation Insights" | |
| } | |
| } | |
| def call_llm_api(self, prompt, api_key): | |
| """LLM API๋ฅผ ํธ์ถํ์ฌ ๊ณ ๊ธ ๋ถ์ ์ํ""" | |
| if not api_key: | |
| return None | |
| url = "https://api.fireworks.ai/inference/v1/chat/completions" | |
| payload = { | |
| "model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507", | |
| "max_tokens": 4096, | |
| "top_p": 1, | |
| "top_k": 40, | |
| "presence_penalty": 0, | |
| "frequency_penalty": 0, | |
| "temperature": 0.6, | |
| "messages": [ | |
| { | |
| "role": "system", | |
| "content": "You are an expert startup valuation analyst and strategic advisor with deep knowledge of venture capital, financial analysis, and business strategy." | |
| }, | |
| { | |
| "role": "user", | |
| "content": prompt | |
| } | |
| ] | |
| } | |
| headers = { | |
| "Accept": "application/json", | |
| "Content-Type": "application/json", | |
| "Authorization": f"Bearer {api_key}" | |
| } | |
| try: | |
| response = requests.post(url, headers=headers, data=json.dumps(payload)) | |
| if response.status_code == 200: | |
| return response.json()['choices'][0]['message']['content'] | |
| else: | |
| return None | |
| except: | |
| return None | |
| def generate_strategic_report(self, data, results, language, api_key): | |
| """LLM์ ์ฌ์ฉํ์ฌ ์ ๋ต์ ๋ณด๊ณ ์ ์์ฑ""" | |
| if language == "ko": | |
| prompt = f""" | |
| ๋ค์ ์คํํธ์ ์ ๊ฐ์นํ๊ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ถ์ํ๊ณ ์ ๋ต์ ์กฐ์ธ์ ํฌํจํ ์์ธ ๋ณด๊ณ ์๋ฅผ ์์ฑํด์ฃผ์ธ์: | |
| ํ์ฌ ์ ๋ณด: | |
| - ํ์ฌ๋ช : {data['company_name']} | |
| - ์ค๋ฆฝ๋ ๋: {data['founded_year']} | |
| - ์ฐ์ : {data['industry']} | |
| - ์ฌ์ ๋จ๊ณ: {data['stage']} | |
| ํ๊ฐ ๊ฒฐ๊ณผ: | |
| - ์ต์ข ๊ธฐ์ ๊ฐ์น: ${results['final_valuation']/1000000:.2f}M | |
| - ๋ฒํฌ์ค ํ๊ฐ: ${results['berkus_valuation']/1000000:.2f}M | |
| - ARR: ${results['arr']/1000000:.2f}M | |
| - ์ฑ์ฅ๋ฅ : {data['growth_rate']}% | |
| - LTV/CAC: {results['ltv_cac_ratio']:.1f} | |
| - ๋ฐ์จ์ด: {results['runway']:.1f}๊ฐ์ | |
| - ์ค์ฝ์ด์นด๋ ์ ์๋ค: {results['scorecard_adjustments']} | |
| ๋ค์์ ํฌํจํ์ฌ ์์ฑํด์ฃผ์ธ์: | |
| 1. ๊ฐ์นํ๊ฐ ๊ฒฐ๊ณผ์ ํ๋น์ฑ ๋ถ์ | |
| 2. ๋์ข ์ ๊ณ ๋๋น ํฌ์ง์ ๋ | |
| 3. ์ฃผ์ ๊ฐ์ ๊ณผ ๊ฐ์ ํ์ ์์ญ | |
| 4. ํฅํ 6-12๊ฐ์ ์ ๋ต์ ์ฐ์ ์์ | |
| 5. ์๊ธ์กฐ๋ฌ ์ ๋ต ๋ฐ ์ ์ ์กฐ๋ฌ ๊ท๋ชจ | |
| 6. ์ฃผ์ ๋ฆฌ์คํฌ์ ์ํ ๋ฐฉ์ | |
| 7. ํต์ฌ KPI์ ๋ง์ผ์คํค ์ ์ | |
| """ | |
| else: | |
| prompt = f""" | |
| Please analyze the following startup valuation results and provide a comprehensive strategic report: | |
| Company Information: | |
| - Company Name: {data['company_name']} | |
| - Founded: {data['founded_year']} | |
| - Industry: {data['industry']} | |
| - Stage: {data['stage']} | |
| Valuation Results: | |
| - Final Valuation: ${results['final_valuation']/1000000:.2f}M | |
| - Berkus Valuation: ${results['berkus_valuation']/1000000:.2f}M | |
| - ARR: ${results['arr']/1000000:.2f}M | |
| - Growth Rate: {data['growth_rate']}% | |
| - LTV/CAC: {results['ltv_cac_ratio']:.1f} | |
| - Runway: {results['runway']:.1f} months | |
| - Scorecard Scores: {results['scorecard_adjustments']} | |
| Please include: | |
| 1. Valuation validity analysis | |
| 2. Industry positioning | |
| 3. Key strengths and improvement areas | |
| 4. Strategic priorities for next 6-12 months | |
| 5. Fundraising strategy and optimal round size | |
| 6. Key risks and mitigation strategies | |
| 7. Core KPIs and milestone recommendations | |
| """ | |
| llm_response = self.call_llm_api(prompt, api_key) | |
| return llm_response | |
| def calculate_berkus_score(self, berkus_data): | |
| """๋ฒํฌ์ค ๋ฐฉ๋ฒ์ผ๋ก ํ๊ฐ (์ต๋ $2.5M)""" | |
| scores = {} | |
| total = 0 | |
| # 1. ๊ฑด์ ํ ์์ด๋์ด (Sound Idea) | |
| idea_score = min(100, berkus_data["idea_validation"] + berkus_data["market_research"] * 10) | |
| scores["sound_idea"] = self.berkus_max_values["sound_idea"] * (idea_score / 100) | |
| # 2. ํ๋กํ ํ์ (Prototype) | |
| prototype_score = 0 | |
| if berkus_data["prototype_stage"] == "์์": | |
| prototype_score = 0 | |
| elif berkus_data["prototype_stage"] == "์ปจ์ /๋ชฉ์ ": | |
| prototype_score = 30 | |
| elif berkus_data["prototype_stage"] == "์๋ ํ๋กํ ํ์ ": | |
| prototype_score = 60 | |
| elif berkus_data["prototype_stage"] == "๋ฒ ํ ๋ฒ์ ": | |
| prototype_score = 80 | |
| elif berkus_data["prototype_stage"] == "์ถ์ ๋ฒ์ ": | |
| prototype_score = 100 | |
| scores["prototype"] = self.berkus_max_values["prototype"] * (prototype_score / 100) | |
| # 3. ์ฐ์ํ ํ (Quality Team) | |
| team_score = min(100, | |
| min(berkus_data["team_experience"], 10) * 10 + # ์ต๋ 10๋ ๊น์ง๋ง ๊ฐ์ฐ | |
| berkus_data["domain_expertise"] * 15 + | |
| berkus_data["startup_experience"] * 15 | |
| ) | |
| scores["quality_team"] = self.berkus_max_values["quality_team"] * (team_score / 100) | |
| # 4. ์ ๋ต์ ๊ด๊ณ (Strategic Relationships) | |
| relationship_score = min(100, | |
| berkus_data["partnerships"] * 15 + | |
| berkus_data["advisors"] * 10 + | |
| berkus_data["pilot_customers"] * 25 | |
| ) | |
| scores["strategic_relationships"] = self.berkus_max_values["strategic_relationships"] * (relationship_score / 100) | |
| # 5. ์ ํ ์ถ์/ํ๋งค (Product Rollout) | |
| if berkus_data["sales_started"]: | |
| rollout_score = min(100, 50 + berkus_data["customer_validation"] * 10) | |
| else: | |
| rollout_score = berkus_data["launch_readiness"] | |
| scores["product_rollout"] = self.berkus_max_values["product_rollout"] * (rollout_score / 100) | |
| total = sum(scores.values()) | |
| return total, scores | |
| def calculate_scorecard_valuation(self, scorecard_data, base_valuation): | |
| """์ค์ฝ์ด์นด๋ ๋ฐฉ๋ฒ์ผ๋ก ์กฐ์ ๋ ๊ฐ์นํ๊ฐ""" | |
| adjustments = {} | |
| # ๊ฐ ์์๋ณ ์กฐ์ ๋น์จ ๊ณ์ฐ (0.5 ~ 1.5) | |
| adjustments["team"] = scorecard_data["team_strength"] / 100 | |
| adjustments["market_size"] = scorecard_data["market_opportunity"] / 100 | |
| adjustments["product"] = scorecard_data["product_stage"] / 100 | |
| adjustments["competition"] = scorecard_data["competitive_advantage"] / 100 | |
| adjustments["marketing"] = scorecard_data["marketing_channels"] / 100 | |
| adjustments["need_for_funding"] = scorecard_data["funding_efficiency"] / 100 | |
| adjustments["other"] = scorecard_data["other_factors"] / 100 | |
| # ๊ฐ์ค ํ๊ท ๊ณ์ฐ | |
| weighted_score = 0 | |
| for factor, weight in self.scorecard_weights.items(): | |
| # ๊ฐ ์ ์๋ฅผ 0.5 ~ 1.5 ๋ฒ์๋ก ๋ณํ (50์ ์ด 1.0) | |
| adjusted_score = 0.5 + (adjustments[factor]) | |
| weighted_score += adjusted_score * weight | |
| # ๊ธฐ๋ณธ ๊ฐ์นํ๊ฐ์ ์กฐ์ ๋น์จ ์ ์ฉ | |
| adjusted_valuation = base_valuation * weighted_score | |
| return adjusted_valuation, adjustments, weighted_score | |
| def calculate_arr(self, monthly_revenue, revenue_type): | |
| """์ ๋งค์ถ์ ์ฐ๊ฐ ๋ฐ๋ณต ๋งค์ถ(ARR)๋ก ๋ณํ""" | |
| if revenue_type == "๊ตฌ๋ ํ (SaaS)": | |
| return monthly_revenue * 12 | |
| elif revenue_type == "๊ฑฐ๋์์๋ฃํ": | |
| return monthly_revenue * 12 * 0.8 | |
| else: | |
| return monthly_revenue * 12 * 0.6 | |
| def calculate_ltv(self, arpu, gross_margin, monthly_churn): | |
| """LTV ๊ณ์ฐ""" | |
| if monthly_churn == 0: | |
| monthly_churn = 0.01 | |
| return arpu * (gross_margin / 100) / monthly_churn | |
| def calculate_cac(self, monthly_marketing, monthly_sales, new_customers): | |
| """CAC ๊ณ์ฐ""" | |
| if new_customers == 0: | |
| return 0 | |
| return (monthly_marketing + monthly_sales) / new_customers | |
| def calculate_payback(self, cac, arpu, gross_margin): | |
| """Payback Period ๊ณ์ฐ (๊ฐ์)""" | |
| if arpu * (gross_margin / 100) == 0: | |
| return 999 | |
| return cac / (arpu * (gross_margin / 100)) | |
| def calculate_valuation(self, data, berkus_data, scorecard_data, use_revenue_multiple=True): | |
| """์ข ํฉ ๊ฐ์นํ๊ฐ ๊ณ์ฐ""" | |
| results = {} | |
| # 1. ๋ฒํฌ์ค ๋ฐฉ๋ฒ ํ๊ฐ | |
| berkus_valuation, berkus_scores = self.calculate_berkus_score(berkus_data) | |
| results["berkus_valuation"] = berkus_valuation | |
| results["berkus_scores"] = berkus_scores | |
| # 2. ๋งค์ถ ๊ธฐ๋ฐ ํ๊ฐ (๋งค์ถ์ด ์๋ ๊ฒฝ์ฐ) | |
| if data["monthly_revenue"] > 0 and use_revenue_multiple: | |
| # ARR ๊ณ์ฐ | |
| arr = self.calculate_arr(data["monthly_revenue"], data["revenue_type"]) | |
| # ๋จ์๊ฒฝ์ ๊ณ์ฐ | |
| ltv = self.calculate_ltv(data["arpu"], data["gross_margin"], data["monthly_churn"]) | |
| cac = self.calculate_cac(data["monthly_marketing"], data["monthly_sales"], data["new_customers"]) | |
| ltv_cac_ratio = ltv / cac if cac > 0 else 0 | |
| payback = self.calculate_payback(cac, data["arpu"], data["gross_margin"]) | |
| # ๋ฉํฐํ ๊ฒฐ์ | |
| multiples = self.industry_multiples[data["industry"]] | |
| growth_category = self.get_growth_category(data["growth_rate"]) | |
| growth_adj = self.growth_adjustments[growth_category] | |
| # ๊ธฐ๋ณธ ๋ฉํฐํ ์ ํ | |
| if ltv_cac_ratio >= 3: | |
| base_multiple = multiples["high"] | |
| elif ltv_cac_ratio >= 1.5: | |
| base_multiple = multiples["mid"] | |
| else: | |
| base_multiple = multiples["low"] | |
| adjusted_multiple = base_multiple * growth_adj | |
| # ๋งค์ถ ๊ธฐ๋ฐ ๊ฐ์นํ๊ฐ | |
| revenue_valuation = arr * adjusted_multiple | |
| results["arr"] = arr | |
| results["ltv"] = ltv | |
| results["cac"] = cac | |
| results["ltv_cac_ratio"] = ltv_cac_ratio | |
| results["payback"] = payback | |
| results["multiple"] = adjusted_multiple | |
| else: | |
| revenue_valuation = 0 | |
| results["arr"] = 0 | |
| results["ltv"] = 0 | |
| results["cac"] = 0 | |
| results["ltv_cac_ratio"] = 0 | |
| results["payback"] = 0 | |
| results["multiple"] = 0 | |
| # 3. ๊ธฐ๋ณธ ๊ฐ์นํ๊ฐ ๊ฒฐ์ (๋ฒํฌ์ค vs ๋งค์ถ ๊ธฐ๋ฐ) | |
| if revenue_valuation > berkus_valuation * 1.5: | |
| base_valuation = revenue_valuation | |
| valuation_method = "revenue_multiple" | |
| else: | |
| base_valuation = max(berkus_valuation, revenue_valuation) | |
| valuation_method = "berkus" | |
| # 4. ์ค์ฝ์ด์นด๋ ์กฐ์ | |
| final_valuation, scorecard_adjustments, weighted_score = self.calculate_scorecard_valuation( | |
| scorecard_data, base_valuation | |
| ) | |
| results["base_valuation"] = base_valuation | |
| results["final_valuation"] = final_valuation | |
| results["valuation_method"] = valuation_method | |
| results["scorecard_adjustments"] = scorecard_adjustments | |
| results["scorecard_multiplier"] = weighted_score | |
| # 5. ๋ฐ์จ์ด ๊ณ์ฐ | |
| results["runway"] = data["cash_balance"] / data["burn_rate"] if data["burn_rate"] > 0 else 999 | |
| return results | |
| def get_growth_category(self, growth_rate): | |
| """์ฑ์ฅ๋ฅ ์นดํ ๊ณ ๋ฆฌ ๊ฒฐ์ """ | |
| if growth_rate < 20: | |
| return "0-20%" | |
| elif growth_rate < 50: | |
| return "20-50%" | |
| elif growth_rate < 100: | |
| return "50-100%" | |
| elif growth_rate < 200: | |
| return "100-200%" | |
| else: | |
| return "200%+" | |
| def create_valuation_comparison_chart(self, results, language="ko"): | |
| """ํ๊ฐ ๋ฐฉ๋ฒ๋ณ ๋น๊ต ์ฐจํธ""" | |
| fig = go.Figure() | |
| methods = ["Berkus", "Revenue Multiple", "Scorecard Adjusted"] | |
| values = [ | |
| results["berkus_valuation"], | |
| results["base_valuation"] if results["valuation_method"] == "revenue_multiple" else 0, | |
| results["final_valuation"] | |
| ] | |
| fig.add_trace(go.Bar( | |
| x=methods, | |
| y=values, | |
| text=[f"${v/1000000:.2f}M" for v in values], | |
| textposition="outside", | |
| marker_color=["lightblue", "lightgreen", "darkblue"] | |
| )) | |
| title = "ํ๊ฐ ๋ฐฉ๋ฒ๋ณ ๊ธฐ์ ๊ฐ์น ๋น๊ต" if language == "ko" else "Valuation by Method" | |
| fig.update_layout( | |
| title=title, | |
| yaxis_title="Valuation (USD)", | |
| showlegend=False, | |
| height=400 | |
| ) | |
| return fig | |
| def create_scorecard_radar_chart(self, adjustments, language="ko"): | |
| """์ค์ฝ์ด์นด๋ ์์๋ณ ์ ์ ๋ ์ด๋ ์ฐจํธ""" | |
| categories = list(adjustments.keys()) | |
| if language == "ko": | |
| categories_display = ["ํ", "์์ฅ๊ท๋ชจ", "์ ํ", "๊ฒฝ์๋ ฅ", "๋ง์ผํ ", "์๊ธํจ์จ", "๊ธฐํ"] | |
| else: | |
| categories_display = ["Team", "Market", "Product", "Competition", "Marketing", "Funding", "Other"] | |
| values = [adjustments[cat] * 100 for cat in categories] | |
| fig = go.Figure(data=go.Scatterpolar( | |
| r=values, | |
| theta=categories_display, | |
| fill='toself' | |
| )) | |
| title = "์ค์ฝ์ด์นด๋ ํ๊ฐ ์์" if language == "ko" else "Scorecard Factors" | |
| fig.update_layout( | |
| polar=dict( | |
| radialaxis=dict( | |
| visible=True, | |
| range=[0, 100] | |
| )), | |
| showlegend=False, | |
| title=title | |
| ) | |
| return fig | |
| def create_ui(): | |
| calculator = StartupValuationCalculator() | |
| def process_valuation( | |
| api_key, language, | |
| # ๊ธฐ๋ณธ ์ ๋ณด | |
| company_name, founded_year, industry, stage, revenue_type, | |
| # ๋งค์ถ ์ ๋ณด | |
| monthly_revenue, growth_rate, arpu, gross_margin, monthly_churn, | |
| retention_rate, new_customers, monthly_marketing, monthly_sales, | |
| # ์ฌ๋ฌด ์ ๋ณด | |
| cash_balance, burn_rate, | |
| # ๋ฒํฌ์ค ๋ฐฉ๋ฒ ์ ๋ ฅ | |
| idea_validation, market_research, prototype_stage, team_experience, | |
| domain_expertise, startup_experience, partnerships, advisors, | |
| pilot_customers, sales_started, customer_validation, launch_readiness, | |
| # ์ค์ฝ์ด์นด๋ ์ ๋ ฅ | |
| team_strength, market_opportunity, product_stage, competitive_advantage, | |
| marketing_channels, funding_efficiency, other_factors | |
| ): | |
| # ๋ฐ์ดํฐ ์ค๋น | |
| data = { | |
| "company_name": company_name, | |
| "founded_year": founded_year, | |
| "industry": industry, | |
| "stage": stage, | |
| "revenue_type": revenue_type, | |
| "monthly_revenue": monthly_revenue * 1000, | |
| "growth_rate": growth_rate, | |
| "arpu": arpu, | |
| "gross_margin": gross_margin, | |
| "monthly_churn": monthly_churn / 100, | |
| "retention_rate": retention_rate, | |
| "new_customers": new_customers, | |
| "monthly_marketing": monthly_marketing * 1000, | |
| "monthly_sales": monthly_sales * 1000, | |
| "cash_balance": cash_balance * 1000, | |
| "burn_rate": burn_rate * 1000 | |
| } | |
| berkus_data = { | |
| "idea_validation": idea_validation, | |
| "market_research": market_research, | |
| "prototype_stage": prototype_stage, | |
| "team_experience": team_experience, | |
| "domain_expertise": domain_expertise, | |
| "startup_experience": startup_experience, | |
| "partnerships": partnerships, | |
| "advisors": advisors, | |
| "pilot_customers": pilot_customers, | |
| "sales_started": sales_started, | |
| "customer_validation": customer_validation, | |
| "launch_readiness": launch_readiness | |
| } | |
| scorecard_data = { | |
| "team_strength": team_strength, | |
| "market_opportunity": market_opportunity, | |
| "product_stage": product_stage, | |
| "competitive_advantage": competitive_advantage, | |
| "marketing_channels": marketing_channels, | |
| "funding_efficiency": funding_efficiency, | |
| "other_factors": other_factors | |
| } | |
| # ๊ฐ์นํ๊ฐ ๊ณ์ฐ | |
| use_revenue = monthly_revenue > 0 | |
| results = calculator.calculate_valuation(data, berkus_data, scorecard_data, use_revenue) | |
| # ์ธ์ด๋ณ ํ ์คํธ | |
| t = calculator.translations[language] | |
| # ๊ฒฐ๊ณผ ํฌ๋งทํ | |
| if language == "ko": | |
| valuation_text = f""" | |
| # ๐ {company_name} {t['valuation_result']} | |
| ## ๐ ์ข ํฉ ํ๊ฐ | |
| - **{t['company_value']}**: ${results['final_valuation']/1000000:.2f}M | |
| - **ํ๊ฐ ๋ฐฉ๋ฒ**: {'๋งค์ถ ๋ฉํฐํ' if results['valuation_method'] == 'revenue_multiple' else '๋ฒํฌ์ค ๋ฐฉ๋ฒ'} + ์ค์ฝ์ด์นด๋ ์กฐ์ | |
| - **์ค์ฝ์ด์นด๋ ์กฐ์ ๋ฐฐ์**: {results['scorecard_multiplier']:.2f}x | |
| ## ๐ฏ {t['berkus_score']} (์ต๋ $2.5M) | |
| - **์ด ํ๊ฐ์ก**: ${results['berkus_valuation']/1000000:.2f}M | |
| - ๊ฑด์ ํ ์์ด๋์ด: ${results['berkus_scores']['sound_idea']/1000:.0f}K | |
| - ํ๋กํ ํ์ : ${results['berkus_scores']['prototype']/1000:.0f}K | |
| - ์ฐ์ํ ํ: ${results['berkus_scores']['quality_team']/1000:.0f}K | |
| - ์ ๋ต์ ๊ด๊ณ: ${results['berkus_scores']['strategic_relationships']/1000:.0f}K | |
| - ์ ํ ์ถ์: ${results['berkus_scores']['product_rollout']/1000:.0f}K | |
| """ | |
| else: | |
| valuation_text = f""" | |
| # ๐ {company_name} {t['valuation_result']} | |
| ## ๐ Summary | |
| - **{t['company_value']}**: ${results['final_valuation']/1000000:.2f}M | |
| - **Method**: {'Revenue Multiple' if results['valuation_method'] == 'revenue_multiple' else 'Berkus Method'} + Scorecard | |
| - **Scorecard Multiplier**: {results['scorecard_multiplier']:.2f}x | |
| ## ๐ฏ {t['berkus_score']} (Max $2.5M) | |
| - **Total**: ${results['berkus_valuation']/1000000:.2f}M | |
| - Sound Idea: ${results['berkus_scores']['sound_idea']/1000:.0f}K | |
| - Prototype: ${results['berkus_scores']['prototype']/1000:.0f}K | |
| - Quality Team: ${results['berkus_scores']['quality_team']/1000:.0f}K | |
| - Strategic Relationships: ${results['berkus_scores']['strategic_relationships']/1000:.0f}K | |
| - Product Rollout: ${results['berkus_scores']['product_rollout']/1000:.0f}K | |
| """ | |
| # ๋งค์ถ ๊ธฐ๋ฐ ํ๊ฐ ์ถ๊ฐ (๋งค์ถ์ด ์๋ ๊ฒฝ์ฐ) | |
| if use_revenue and results['arr'] > 0: | |
| if language == "ko": | |
| valuation_text += f""" | |
| ## ๐ฐ ๋งค์ถ ๊ธฐ๋ฐ ํ๊ฐ | |
| - **ARR**: ${results['arr']/1000000:.2f}M | |
| - **์ ์ฉ ๋ฉํฐํ**: {results['multiple']:.1f}x | |
| - **LTV/CAC**: {results['ltv_cac_ratio']:.1f}x | |
| - **Payback**: {results['payback']:.1f}๊ฐ์ | |
| """ | |
| else: | |
| valuation_text += f""" | |
| ## ๐ฐ Revenue-based Valuation | |
| - **ARR**: ${results['arr']/1000000:.2f}M | |
| - **Multiple**: {results['multiple']:.1f}x | |
| - **LTV/CAC**: {results['ltv_cac_ratio']:.1f}x | |
| - **Payback**: {results['payback']:.1f} months | |
| """ | |
| # ์ฌ๋ฌด ๊ฑด์ ์ฑ | |
| if language == "ko": | |
| valuation_text += f""" | |
| ## ๐ {t['financial_health']} | |
| - **ํ๊ธ ๋ฐ์จ์ด**: {results['runway']:.1f}๊ฐ์ | |
| - **์๊ฐ ๋ฒ๋ ์ดํธ**: ${burn_rate}K | |
| """ | |
| else: | |
| valuation_text += f""" | |
| ## ๐ {t['financial_health']} | |
| - **Cash Runway**: {results['runway']:.1f} months | |
| - **Monthly Burn Rate**: ${burn_rate}K | |
| """ | |
| # LLM ๊ธฐ๋ฐ ์ ๋ต์ ๋ถ์ ์ถ๊ฐ | |
| strategic_report = None | |
| if api_key and api_key.strip(): | |
| strategic_report = calculator.generate_strategic_report(data, results, language, api_key) | |
| if strategic_report: | |
| if language == "ko": | |
| valuation_text += f""" | |
| ## ๐ค AI ์ ๋ต์ ๋ถ์ | |
| {strategic_report} | |
| """ | |
| else: | |
| valuation_text += f""" | |
| ## ๐ค AI Strategic Analysis | |
| {strategic_report} | |
| """ | |
| # ์ฐจํธ ์์ฑ | |
| comparison_chart = calculator.create_valuation_comparison_chart(results, language) | |
| scorecard_chart = calculator.create_scorecard_radar_chart(results['scorecard_adjustments'], language) | |
| # ์์ธ ํ ์ด๋ธ | |
| if language == "ko": | |
| methods_df = pd.DataFrame({ | |
| "ํ๊ฐ ๋ฐฉ๋ฒ": ["๋ฒํฌ์ค ๋ฐฉ๋ฒ", "๋งค์ถ ๋ฉํฐํ", "์ค์ฝ์ด์นด๋ ์กฐ์ ", "์ต์ข ํ๊ฐ"], | |
| "ํ๊ฐ์ก": [ | |
| f"${results['berkus_valuation']/1000000:.2f}M", | |
| f"${results['base_valuation']/1000000:.2f}M" if results['valuation_method'] == 'revenue_multiple' else "N/A", | |
| f"{results['scorecard_multiplier']:.2f}x", | |
| f"${results['final_valuation']/1000000:.2f}M" | |
| ] | |
| }) | |
| else: | |
| methods_df = pd.DataFrame({ | |
| "Method": ["Berkus Method", "Revenue Multiple", "Scorecard Adjustment", "Final Valuation"], | |
| "Value": [ | |
| f"${results['berkus_valuation']/1000000:.2f}M", | |
| f"${results['base_valuation']/1000000:.2f}M" if results['valuation_method'] == 'revenue_multiple' else "N/A", | |
| f"{results['scorecard_multiplier']:.2f}x", | |
| f"${results['final_valuation']/1000000:.2f}M" | |
| ] | |
| }) | |
| return valuation_text, comparison_chart, scorecard_chart, methods_df | |
| # Gradio UI | |
| with gr.Blocks(title="Startup Valuation Calculator", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(""" | |
| # ๐ฆ ์คํํธ์ ๊ฐ์นํ๊ฐ ์๋ํ ์์คํ v3.5 | |
| ### AI ๊ธฐ๋ฐ ์ ๋ต์ ๋ถ์์ ํฌํจํ ์ข ํฉ ํ๊ฐ ์์คํ | |
| """) | |
| # API ํค์ ์ธ์ด ์ ํ | |
| with gr.Row(): | |
| api_key = gr.Textbox( | |
| label="Fireworks API Key (์ ํ์ฌํญ - AI ๋ถ์์ฉ)", | |
| placeholder="AI ์ ๋ต์ ๋ถ์์ ์ํ์๋ฉด API ํค๋ฅผ ์ ๋ ฅํ์ธ์", | |
| type="password" | |
| ) | |
| language = gr.Radio( | |
| choices=[("ํ๊ตญ์ด", "ko"), ("English", "en")], | |
| value="ko", | |
| label="Language / ์ธ์ด", | |
| type="value" | |
| ) | |
| with gr.Tab("๊ธฐ๋ณธ ์ ๋ณด / Basic Info"): | |
| with gr.Row(): | |
| company_name = gr.Textbox(label="ํ์ฌ๋ช / Company Name", value="์ฐ๋ฆฌ ์คํํธ์ ") | |
| founded_year = gr.Slider(2000, 2025, value=2022, step=1, label="์ค๋ฆฝ์ฐ๋ / Founded Year") | |
| with gr.Row(): | |
| industry = gr.Dropdown( | |
| choices=list(calculator.industry_multiples.keys()), | |
| value="SaaS - B2B", | |
| label="์ฐ์ ๋ถ๋ฅ / Industry" | |
| ) | |
| stage = gr.Radio( | |
| choices=["MVP/๋ฒ ํ", "์ด๊ธฐ ๋งค์ถ", "์ฑ์ฅ ๋จ๊ณ", "์์ต์ฑ ํ๋ณด"], | |
| value="์ด๊ธฐ ๋งค์ถ", | |
| label="์ฌ์ ๋จ๊ณ / Stage" | |
| ) | |
| revenue_type = gr.Radio( | |
| choices=["๊ตฌ๋ ํ (SaaS)", "๊ฑฐ๋์์๋ฃํ", "์ผํ์ฑ ํ๋งค"], | |
| value="๊ตฌ๋ ํ (SaaS)", | |
| label="์์ต ๋ชจ๋ธ / Revenue Model" | |
| ) | |
| with gr.Tab("๋ฒํฌ์ค ํ๊ฐ / Berkus Method"): | |
| gr.Markdown("### ๐ก ์์ด๋์ด ๊ฒ์ฆ / Idea Validation") | |
| with gr.Row(): | |
| idea_validation = gr.Slider(0, 100, value=70, step=10, | |
| label="์์ด๋์ด ๊ฒ์ฆ ์์ค / Idea Validation Level (%)") | |
| market_research = gr.Slider(0, 10, value=5, step=1, | |
| label="์์ฅ ์กฐ์ฌ ๊น์ด / Market Research Depth (1-10)") | |
| gr.Markdown("### ๐ง ํ๋กํ ํ์ / Prototype") | |
| prototype_stage = gr.Radio( | |
| choices=["์์", "์ปจ์ /๋ชฉ์ ", "์๋ ํ๋กํ ํ์ ", "๋ฒ ํ ๋ฒ์ ", "์ถ์ ๋ฒ์ "], | |
| value="๋ฒ ํ ๋ฒ์ ", | |
| label="ํ๋กํ ํ์ ๋จ๊ณ / Prototype Stage" | |
| ) | |
| gr.Markdown("### ๐ฅ ํ ์ญ๋ / Team Quality") | |
| with gr.Row(): | |
| team_experience = gr.Slider(0, 30, value=5, step=1, | |
| label="ํ ํ๊ท ๊ฒฝ๋ ฅ(๋ ) / Average Experience (years)") | |
| domain_expertise = gr.Slider(0, 5, value=3, step=1, | |
| label="๋๋ฉ์ธ ์ ๋ฌธ์ฑ / Domain Expertise (1-5)") | |
| startup_experience = gr.Slider(0, 5, value=2, step=1, | |
| label="์คํํธ์ ๊ฒฝํ / Startup Experience (1-5)") | |
| gr.Markdown("### ๐ค ์ ๋ต์ ๊ด๊ณ / Strategic Relationships") | |
| with gr.Row(): | |
| partnerships = gr.Number(label="์ ๋ต์ ํํธ๋์ญ ์ / Strategic Partnerships", value=2) | |
| advisors = gr.Number(label="๊ณ ๋ฌธ/๋ฉํ ์ / Advisors/Mentors", value=3) | |
| pilot_customers = gr.Number(label="ํ์ผ๋ฟ ๊ณ ๊ฐ ์ / Pilot Customers", value=5) | |
| gr.Markdown("### ๐ ์ ํ ์ถ์ / Product Rollout") | |
| with gr.Row(): | |
| sales_started = gr.Checkbox(label="๋งค์ถ ๋ฐ์ ์์ / Sales Started", value=True) | |
| customer_validation = gr.Slider(0, 10, value=5, step=1, | |
| label="๊ณ ๊ฐ ๊ฒ์ฆ ์์ค / Customer Validation (1-10)") | |
| launch_readiness = gr.Slider(0, 100, value=80, step=10, | |
| label="์ถ์ ์ค๋น๋ / Launch Readiness (%)") | |
| with gr.Tab("์ค์ฝ์ด์นด๋ ํ๊ฐ / Scorecard"): | |
| gr.Markdown("### ๊ฐ ์์๋ฅผ ๋์ผ ์คํ ์ด์ง ํ๊ท ๋๋น ํ๊ฐ (50 = ํ๊ท )") | |
| gr.Markdown("### Rate each factor compared to same-stage average (50 = average)") | |
| team_strength = gr.Slider(0, 100, value=60, step=5, | |
| label="ํ ์ญ๋ / Team Strength") | |
| market_opportunity = gr.Slider(0, 100, value=70, step=5, | |
| label="์์ฅ ๊ธฐํ / Market Opportunity") | |
| product_stage = gr.Slider(0, 100, value=65, step=5, | |
| label="์ ํ ์์ฑ๋ / Product Maturity") | |
| competitive_advantage = gr.Slider(0, 100, value=55, step=5, | |
| label="๊ฒฝ์ ์ฐ์ / Competitive Advantage") | |
| marketing_channels = gr.Slider(0, 100, value=50, step=5, | |
| label="๋ง์ผํ /ํ๋งค / Marketing & Sales") | |
| funding_efficiency = gr.Slider(0, 100, value=60, step=5, | |
| label="์๊ธ ํจ์จ์ฑ / Funding Efficiency") | |
| other_factors = gr.Slider(0, 100, value=50, step=5, | |
| label="๊ธฐํ ์์ / Other Factors") | |
| with gr.Tab("๋งค์ถ ์ ๋ณด / Revenue (Optional)"): | |
| gr.Markdown("### ๐ฐ ๋งค์ถ์ด ์๋ ๊ฒฝ์ฐ๋ง ์ ๋ ฅ / Only if you have revenue") | |
| with gr.Row(): | |
| monthly_revenue = gr.Number(label="์ ๋งค์ถ / Monthly Revenue ($K)", value=0) | |
| growth_rate = gr.Slider(0, 300, value=0, step=10, | |
| label="์ฐ๊ฐ ์ฑ์ฅ๋ฅ / Annual Growth Rate (%)") | |
| with gr.Row(): | |
| arpu = gr.Number(label="ARPU ($)", value=0) | |
| gross_margin = gr.Slider(0, 100, value=0, step=5, | |
| label="๋งค์ถ์ด์ด์ต๋ฅ / Gross Margin (%)") | |
| with gr.Row(): | |
| retention_rate = gr.Slider(0, 100, value=0, step=5, | |
| label="๊ณ ๊ฐ ์ ์ง์จ / Retention Rate (%)") | |
| monthly_churn = gr.Slider(0, 20, value=0, step=0.5, | |
| label="์ ์ดํ๋ฅ / Monthly Churn (%)") | |
| with gr.Row(): | |
| new_customers = gr.Number(label="์ ์ ๊ท ๊ณ ๊ฐ / New Customers/Month", value=0) | |
| monthly_marketing = gr.Number(label="์ ๋ง์ผํ ๋น์ฉ / Marketing Cost ($K)", value=0) | |
| monthly_sales = gr.Number(label="์ ์์ ๋น์ฉ / Sales Cost ($K)", value=0) | |
| with gr.Tab("์ฌ๋ฌด ํํฉ / Financials"): | |
| gr.Markdown("### ๐ธ ํ๊ธ ์ํฉ / Cash Position ($K)") | |
| with gr.Row(): | |
| cash_balance = gr.Number(label="ํ๊ธ ์๊ณ / Cash Balance ($K)", value=1000) | |
| burn_rate = gr.Number(label="์ ๋ฒ๋ ์ดํธ / Monthly Burn Rate ($K)", value=80) | |
| # ํ๊ฐ ์คํ ๋ฒํผ | |
| evaluate_btn = gr.Button("๐ ๊ฐ์นํ๊ฐ ์คํ / Run Valuation", variant="primary", size="lg") | |
| # ๊ฒฐ๊ณผ ์ถ๋ ฅ | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| valuation_output = gr.Markdown(label="ํ๊ฐ ๊ฒฐ๊ณผ / Results") | |
| with gr.Column(scale=1): | |
| methods_table = gr.DataFrame(label="ํ๊ฐ ๋ฐฉ๋ฒ ๋น๊ต / Method Comparison") | |
| with gr.Row(): | |
| comparison_chart = gr.Plot(label="ํ๊ฐ ๋ฐฉ๋ฒ ๋น๊ต / Valuation Comparison") | |
| scorecard_chart = gr.Plot(label="์ค์ฝ์ด์นด๋ ๋ถ์ / Scorecard Analysis") | |
| # ์ด๋ฒคํธ ์ฐ๊ฒฐ | |
| evaluate_btn.click( | |
| process_valuation, | |
| inputs=[ | |
| api_key, language, | |
| company_name, founded_year, industry, stage, revenue_type, | |
| monthly_revenue, growth_rate, arpu, gross_margin, monthly_churn, | |
| retention_rate, new_customers, monthly_marketing, monthly_sales, | |
| cash_balance, burn_rate, | |
| idea_validation, market_research, prototype_stage, team_experience, | |
| domain_expertise, startup_experience, partnerships, advisors, | |
| pilot_customers, sales_started, customer_validation, launch_readiness, | |
| team_strength, market_opportunity, product_stage, competitive_advantage, | |
| marketing_channels, funding_efficiency, other_factors | |
| ], | |
| outputs=[valuation_output, comparison_chart, scorecard_chart, methods_table] | |
| ) | |
| # ์์ ๋ฐ์ดํฐ | |
| gr.Markdown("### ๐ ์์ ๋ฐ์ดํฐ / Example Data") | |
| with gr.Row(): | |
| gr.Button("์ด๊ธฐ ์คํํธ์ / Early Startup").click( | |
| lambda: [ | |
| "", "ko", "ํ ํฌ ์คํํธ์ ", 2023, "AI/๋ฅํ ํฌ", "MVP/๋ฒ ํ", "๊ตฌ๋ ํ (SaaS)", | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 500, 50, | |
| 80, 7, "๋ฒ ํ ๋ฒ์ ", 3, 4, 1, 1, 2, 3, False, 0, 70, | |
| 70, 65, 55, 60, 45, 50, 50 | |
| ], | |
| outputs=[ | |
| api_key, language, company_name, founded_year, industry, stage, revenue_type, | |
| monthly_revenue, growth_rate, arpu, gross_margin, monthly_churn, | |
| retention_rate, new_customers, monthly_marketing, monthly_sales, | |
| cash_balance, burn_rate, | |
| idea_validation, market_research, prototype_stage, team_experience, | |
| domain_expertise, startup_experience, partnerships, advisors, | |
| pilot_customers, sales_started, customer_validation, launch_readiness, | |
| team_strength, market_opportunity, product_stage, competitive_advantage, | |
| marketing_channels, funding_efficiency, other_factors | |
| ] | |
| ) | |
| gr.Button("์ฑ์ฅ ๋จ๊ณ / Growth Stage").click( | |
| lambda: [ | |
| "", "en", "SaaS Corp", 2021, "SaaS - B2B", "์ฑ์ฅ ๋จ๊ณ", "๊ตฌ๋ ํ (SaaS)", | |
| 100, 150, 200, 75, 2, 90, 40, 30, 20, 2000, 120, | |
| 90, 9, "์ถ์ ๋ฒ์ ", 8, 5, 3, 5, 5, 20, True, 8, 95, | |
| 85, 80, 75, 70, 65, 75, 60 | |
| ], | |
| outputs=[ | |
| api_key, language, company_name, founded_year, industry, stage, revenue_type, | |
| monthly_revenue, growth_rate, arpu, gross_margin, monthly_churn, | |
| retention_rate, new_customers, monthly_marketing, monthly_sales, | |
| cash_balance, burn_rate, | |
| idea_validation, market_research, prototype_stage, team_experience, | |
| domain_expertise, startup_experience, partnerships, advisors, | |
| pilot_customers, sales_started, customer_validation, launch_readiness, | |
| team_strength, market_opportunity, product_stage, competitive_advantage, | |
| marketing_channels, funding_efficiency, other_factors | |
| ] | |
| ) | |
| return demo | |
| # ์คํ | |
| if __name__ == "__main__": | |
| demo = create_ui() | |
| demo.launch(share=True) |