StartUp-Value / app.py
openfree's picture
Update app.py
0cd8b82 verified
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)