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 | |
| 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 | |
| } | |
| # ๋จ์๊ฒฝ์ ์ ์ ๊ฐ์ค์น | |
| self.unit_economics_weights = { | |
| "ltv_cac_ratio": 0.3, | |
| "gross_margin": 0.3, | |
| "retention": 0.2, | |
| "payback": 0.2 | |
| } | |
| 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 get_unit_economics_score(self, ltv_cac_ratio, gross_margin, retention_rate, payback_months): | |
| """๋จ์๊ฒฝ์ ์ ์ ๊ณ์ฐ (0-100)""" | |
| scores = { | |
| "ltv_cac_ratio": min(100, (ltv_cac_ratio / 3) * 100) if ltv_cac_ratio > 0 else 0, | |
| "gross_margin": min(100, gross_margin * 1.25), | |
| "retention": retention_rate, | |
| "payback": max(0, 100 - (payback_months / 24) * 100) if payback_months < 999 else 0 | |
| } | |
| total_score = sum(scores[key] * self.unit_economics_weights[key] for key in scores) | |
| return total_score | |
| 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 calculate_valuation(self, data): | |
| """์ข ํฉ ๊ฐ์นํ๊ฐ ๊ณ์ฐ""" | |
| # 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"]) | |
| # ๋จ์๊ฒฝ์ ์ ์ | |
| ue_score = self.get_unit_economics_score( | |
| ltv_cac_ratio, data["gross_margin"], data["retention_rate"], payback | |
| ) | |
| # ๊ธฐ๋ณธ ๋ฉํฐํ ์ ํ | |
| multiples = self.industry_multiples[data["industry"]] | |
| if ue_score >= 80: | |
| base_multiple = multiples["high"] | |
| elif ue_score >= 50: | |
| base_multiple = multiples["mid"] | |
| else: | |
| base_multiple = multiples["low"] | |
| # ์ฑ์ฅ๋ฅ ์กฐ์ | |
| growth_adj = self.growth_adjustments[self.get_growth_category(data["growth_rate"])] | |
| adjusted_multiple = base_multiple * growth_adj | |
| # ์คํ ์ด์ง ์กฐ์ | |
| stage_adj = { | |
| "MVP/๋ฒ ํ": 0.7, | |
| "์ด๊ธฐ ๋งค์ถ": 0.85, | |
| "์ฑ์ฅ ๋จ๊ณ": 1.0, | |
| "์์ต์ฑ ํ๋ณด": 1.2 | |
| } | |
| final_multiple = adjusted_multiple * stage_adj[data["stage"]] | |
| # ์ต์ข ๊ฐ์นํ๊ฐ | |
| valuation = arr * final_multiple | |
| # ๋ฐ์จ์ด ๊ณ์ฐ | |
| runway = data["cash_balance"] / data["burn_rate"] if data["burn_rate"] > 0 else 999 | |
| return { | |
| "valuation": valuation, | |
| "arr": arr, | |
| "multiple": final_multiple, | |
| "ltv": ltv, | |
| "cac": cac, | |
| "ltv_cac_ratio": ltv_cac_ratio, | |
| "payback": payback, | |
| "ue_score": ue_score, | |
| "runway": runway | |
| } | |
| def create_comparison_chart(self, valuation, industry, arr): | |
| """๋์ข ์ ๊ณ ๋น๊ต ์ฐจํธ ์์ฑ""" | |
| multiples = self.industry_multiples[industry] | |
| fig = go.Figure() | |
| # ์ ๊ณ ๋ฒ์ | |
| low_val = arr * multiples["low"] | |
| mid_val = arr * multiples["mid"] | |
| high_val = arr * multiples["high"] | |
| # ๋ง๋ ๊ทธ๋ํ | |
| fig.add_trace(go.Bar( | |
| x=["ํ์ 25%", "์ค๊ฐ๊ฐ", "์์ 25%", "ํ์ฌ ๊ธฐ์ "], | |
| y=[low_val, mid_val, high_val, valuation], | |
| text=[f"${low_val/1000000:.1f}M", f"${mid_val/1000000:.1f}M", | |
| f"${high_val/1000000:.1f}M", f"${valuation/1000000:.1f}M"], | |
| textposition="outside", | |
| marker_color=["lightgray", "gray", "darkgray", "blue"] | |
| )) | |
| fig.update_layout( | |
| title=f"{industry} ์ ๊ณ ๊ฐ์นํ๊ฐ ๋น๊ต", | |
| yaxis_title="๊ธฐ์ ๊ฐ์น (USD)", | |
| showlegend=False, | |
| height=400 | |
| ) | |
| return fig | |
| def create_ui(): | |
| calculator = StartupValuationCalculator() | |
| def process_valuation( | |
| 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 | |
| ): | |
| # ์ ๋ ฅ๊ฐ ๊ฒ์ฆ | |
| if monthly_revenue <= 0: | |
| return "์ ๋งค์ถ์ ์ ๋ ฅํด์ฃผ์ธ์.", None, None | |
| # ๋ฐ์ดํฐ ์ค๋น | |
| 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 | |
| } | |
| # ๊ฐ์นํ๊ฐ ๊ณ์ฐ | |
| results = calculator.calculate_valuation(data) | |
| # ๊ฒฐ๊ณผ ํฌ๋งทํ | |
| valuation_text = f""" | |
| # ๐ {company_name} ๊ฐ์นํ๊ฐ ๊ฒฐ๊ณผ | |
| ## ๐ ์ฃผ์ ์งํ | |
| - **๊ธฐ์ ๊ฐ์น**: ${results['valuation']/1000000:.1f}M (โฉ{results['valuation']/1000000*1300:.0f}์ต) | |
| - **ARR**: ${results['arr']/1000000:.1f}M | |
| - **์ ์ฉ ๋ฉํฐํ**: {results['multiple']:.1f}x | |
| ## ๐ฐ ๋จ์๊ฒฝ์ | |
| - **LTV**: ${results['ltv']:.0f} | |
| - **CAC**: ${results['cac']:.0f} | |
| - **LTV/CAC**: {results['ltv_cac_ratio']:.1f}x | |
| - **Payback Period**: {results['payback']:.1f}๊ฐ์ | |
| - **๋จ์๊ฒฝ์ ์ ์**: {results['ue_score']:.0f}/100 | |
| ## ๐ ์ฌ๋ฌด ๊ฑด์ ์ฑ | |
| - **ํ๊ธ ๋ฐ์จ์ด**: {results['runway']:.1f}๊ฐ์ | |
| - **์๊ฐ ๋ฒ๋ ์ดํธ**: ${burn_rate}K | |
| ## ๐ก ํ๊ฐ ์ธ์ฌ์ดํธ | |
| """ | |
| # ์ธ์ฌ์ดํธ ์ถ๊ฐ | |
| if results['ltv_cac_ratio'] < 1: | |
| valuation_text += "- โ ๏ธ LTV/CAC ๋น์จ์ด 1 ๋ฏธ๋ง์ ๋๋ค. ๋ง์ผํ ํจ์จ์ฑ ๊ฐ์ ์ด ํ์ํฉ๋๋ค.\n" | |
| elif results['ltv_cac_ratio'] > 3: | |
| valuation_text += "- โ ์ฐ์ํ LTV/CAC ๋น์จ์ ๋ณด์ด๊ณ ์์ต๋๋ค.\n" | |
| if results['runway'] < 12: | |
| valuation_text += "- โ ๏ธ ๋ฐ์จ์ด๊ฐ 12๊ฐ์ ๋ฏธ๋ง์ ๋๋ค. ์ถ๊ฐ ์๊ธ์กฐ๋ฌ์ ๊ณ ๋ คํ์ธ์.\n" | |
| if gross_margin < 60: | |
| valuation_text += "- ๐ ๋งค์ถ์ด์ด์ต๋ฅ ๊ฐ์ ์ฌ์ง๊ฐ ์์ต๋๋ค. (์ ๊ณ ํ๊ท : 70-80%)\n" | |
| # ๋น๊ต ์ฐจํธ ์์ฑ | |
| comparison_chart = calculator.create_comparison_chart( | |
| results['valuation'], industry, results['arr'] | |
| ) | |
| # ์์ธ ๋ถ์ ํ ์ด๋ธ | |
| metrics_df = pd.DataFrame({ | |
| "์งํ": ["์ ๋งค์ถ", "์ฐ ์ฑ์ฅ๋ฅ ", "๋งค์ถ์ด์ด์ต๋ฅ ", "์ ์ดํ๋ฅ ", "๊ณ ๊ฐ ์ ์ง์จ"], | |
| "ํ์ฌ ๊ฐ": [f"${monthly_revenue}K", f"{growth_rate}%", f"{gross_margin}%", | |
| f"{monthly_churn}%", f"{retention_rate}%"], | |
| "์ ๊ณ ํ๊ท ": ["N/A", "50-100%", "70-80%", "2-5%", "80-90%"] | |
| }) | |
| return valuation_text, comparison_chart, metrics_df | |
| # Gradio UI | |
| with gr.Blocks(title="์คํํธ์ ๊ฐ์นํ๊ฐ ๊ณ์ฐ๊ธฐ", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(""" | |
| # ๐ฆ ์คํํธ์ ๊ฐ์นํ๊ฐ ์๋ํ ์์คํ | |
| ๊ฐ๋จํ ์ ๋ณด ์ ๋ ฅ๋ง์ผ๋ก ๊ท์ฌ์ ์์ ๊ธฐ์ ๊ฐ์น๋ฅผ ์ฐ์ถํ๊ณ ๋์ข ์ ๊ณ์ ๋น๊ตํด๋๋ฆฝ๋๋ค. | |
| """) | |
| with gr.Tab("๊ธฐ๋ณธ ์ ๋ณด"): | |
| with gr.Row(): | |
| company_name = gr.Textbox(label="ํ์ฌ๋ช ", value="์ฐ๋ฆฌ ์คํํธ์ ") | |
| founded_year = gr.Slider(2015, 2024, value=2022, step=1, label="์ค๋ฆฝ์ฐ๋") | |
| with gr.Row(): | |
| industry = gr.Dropdown( | |
| choices=list(calculator.industry_multiples.keys()), | |
| value="SaaS - B2B", | |
| label="์ฐ์ ๋ถ๋ฅ" | |
| ) | |
| stage = gr.Radio( | |
| choices=["MVP/๋ฒ ํ", "์ด๊ธฐ ๋งค์ถ", "์ฑ์ฅ ๋จ๊ณ", "์์ต์ฑ ํ๋ณด"], | |
| value="์ด๊ธฐ ๋งค์ถ", | |
| label="์ฌ์ ๋จ๊ณ" | |
| ) | |
| revenue_type = gr.Radio( | |
| choices=["๊ตฌ๋ ํ (SaaS)", "๊ฑฐ๋์์๋ฃํ", "์ผํ์ฑ ํ๋งค"], | |
| value="๊ตฌ๋ ํ (SaaS)", | |
| label="์์ต ๋ชจ๋ธ" | |
| ) | |
| with gr.Tab("๋งค์ถ ๋ฐ ์ฑ์ฅ"): | |
| gr.Markdown("### ๐ฐ ๋งค์ถ ์ ๋ณด (๋จ์: ์ฒ ๋ฌ๋ฌ)") | |
| with gr.Row(): | |
| monthly_revenue = gr.Number(label="์ ๋งค์ถ ($K)", value=50) | |
| growth_rate = gr.Slider(0, 300, value=100, step=10, | |
| label="์ฐ๊ฐ ์ฑ์ฅ๋ฅ (%)") | |
| with gr.Row(): | |
| arpu = gr.Number(label="๊ณ ๊ฐ๋น ํ๊ท ๋งค์ถ (ARPU) ($)", value=100) | |
| gross_margin = gr.Slider(0, 100, value=70, step=5, | |
| label="๋งค์ถ์ด์ด์ต๋ฅ (%)") | |
| with gr.Tab("๊ณ ๊ฐ ๋ฐ ๋ง์ผํ "): | |
| gr.Markdown("### ๐ฅ ๊ณ ๊ฐ ์งํ") | |
| with gr.Row(): | |
| retention_rate = gr.Slider(0, 100, value=85, step=5, | |
| label="์๊ฐ ๊ณ ๊ฐ ์ ์ง์จ (%)") | |
| monthly_churn = gr.Slider(0, 20, value=3, step=0.5, | |
| label="์ ์ดํ๋ฅ (%)") | |
| gr.Markdown("### ๐ข ๋ง์ผํ ํจ์จ์ฑ") | |
| with gr.Row(): | |
| new_customers = gr.Number(label="์ ์ ๊ท ๊ณ ๊ฐ ์", value=50) | |
| monthly_marketing = gr.Number(label="์ ๋ง์ผํ ๋น์ฉ ($K)", value=20) | |
| monthly_sales = gr.Number(label="์ ์์ ๋น์ฉ ($K)", value=15) | |
| with gr.Tab("์ฌ๋ฌด ํํฉ"): | |
| gr.Markdown("### ๐ธ ํ๊ธ ์ํฉ (๋จ์: ์ฒ ๋ฌ๋ฌ)") | |
| with gr.Row(): | |
| cash_balance = gr.Number(label="ํ๊ธ ์๊ณ ($K)", value=1000) | |
| burn_rate = gr.Number(label="์ ๋ฒ๋ ์ดํธ ($K)", value=80) | |
| # ํ๊ฐ ์คํ ๋ฒํผ | |
| evaluate_btn = gr.Button("๐ ๊ฐ์นํ๊ฐ ์คํ", variant="primary", size="lg") | |
| # ๊ฒฐ๊ณผ ์ถ๋ ฅ | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| valuation_output = gr.Markdown(label="ํ๊ฐ ๊ฒฐ๊ณผ") | |
| with gr.Column(scale=1): | |
| metrics_table = gr.DataFrame(label="์ฃผ์ ์งํ ๋น๊ต") | |
| comparison_chart = gr.Plot(label="๋์ข ์ ๊ณ ๋น๊ต") | |
| # ์ด๋ฒคํธ ์ฐ๊ฒฐ | |
| evaluate_btn.click( | |
| process_valuation, | |
| inputs=[ | |
| 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 | |
| ], | |
| outputs=[valuation_output, comparison_chart, metrics_table] | |
| ) | |
| # ์์ ๋ฐ์ดํฐ ๋ฒํผ๋ค | |
| gr.Markdown("### ๐ ์์ ๋ฐ์ดํฐ๋ก ํ ์คํธํ๊ธฐ") | |
| with gr.Row(): | |
| gr.Button("SaaS ์คํํธ์ ์์").click( | |
| lambda: [ | |
| "ํ ํฌ ์คํํธ์ ", 2021, "SaaS - B2B", "์ฑ์ฅ ๋จ๊ณ", "๊ตฌ๋ ํ (SaaS)", | |
| 100, 150, 200, 75, 2, | |
| 90, 40, 30, 20, | |
| 2000, 120 | |
| ], | |
| outputs=[ | |
| 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 | |
| ] | |
| ) | |
| gr.Button("์ด์ปค๋จธ์ค ์์").click( | |
| lambda: [ | |
| "์จ๋ผ์ธ ์ผํ๋ชฐ", 2022, "์ด์ปค๋จธ์ค", "์ด๊ธฐ ๋งค์ถ", "์ผํ์ฑ ํ๋งค", | |
| 80, 80, 50, 40, 5, | |
| 70, 100, 25, 10, | |
| 500, 70 | |
| ], | |
| outputs=[ | |
| 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 | |
| ] | |
| ) | |
| return demo | |
| # ์คํ | |
| if __name__ == "__main__": | |
| demo = create_ui() | |
| demo.launch(share=True) |