
基于python实现的便利店投资分析财务建模评估,不仅计算了基础的盈亏,还引入了**现代投资组合理论(Modern Portfolio Theory)**中的关键指标(如夏普比率、索提诺比率),将传统的实体店铺投资分析提升到了量化金融的维度。
实现了一个基于 Python (PyQt5, Matplotlib, NumPy) 的桌面应用程序,专门针对餐饮、零售等实体行业进行深度财务建模。
详细实现逻辑 和核心计算逻辑方式的解析:
1. 概览
该项目采用经典的**MVC(模型-视图-控制器)**架构:
- 视图层 (View) :
InvestmentApp类,基于 PyQt5 实现 GUI 界面,包含输入表单、结果标签页和图表展示。 - 模型层 (Model) :
IndustryBenchmarks:存储各行业的基准数据(黄金比例)。InvestmentCalculator:核心计算引擎。InvestmentCharts:数据可视化引擎。
- 逻辑层 (Logic) :
InvestmentCalculator中的各类计算方法。
2. 核心计算逻辑详解
这是项目最核心的部分,它将实体投资拆解为四个关键的量化分析维度。
A. 初始投资与现金流生成逻辑
这是所有计算的基础,系统首先计算投资者的"入场费"和日常的"造血能力"。
-
初始投资 (Initial Investment) :
Total=转让费+(押金+预付月数)×月租金+装修费+设备费+其他Total = 转让费 + (押金 + 预付月数) \times 月租金 + 装修费 + 设备费 + 其他Total=转让费+(押金+预付月数)×月租金+装修费+设备费+其他- 逻辑细节:代码中将租金拆分为"首期租金",区分了沉没成本(转让费、装修)和流动资金。
-
月度现金流 (Monthly Cash Flow) :
月总收入=(日均最低营收+日均最高营收)/2×运营天数月总收入 = (日均最低营收 + 日均最高营收) / 2 \times 运营天数月总收入=(日均最低营收+日均最高营收)/2×运营天数
月总成本=租金+人力+物料+水电+其他月总成本 = 租金 + 人力 + 物料 + 水电 + 其他月总成本=租金+人力+物料+水电+其他
月净利润=月总收入−月总成本月净利润 = 月总收入 - 月总成本月净利润=月总收入−月总成本- 逻辑细节:成本结构是直接由用户输入的固定金额,而非简单的百分比,这更符合实体经营中"房租、人工"是刚性支出的现实。
B. 回本周期与 ROI (核心生存指标)
这是实体投资者最关心的指标。
-
回本周期 (Payback Period) :
日均利润=月净利润/30日均利润 = 月净利润 / 30日均利润=月净利润/30
回本天数=总初始投资/日均利润回本天数 = 总初始投资 / 日均利润回本天数=总初始投资/日均利润- 逻辑细节 :代码中
calculate_break_even方法还引入了growth_rate(增长率),模拟了收入随时间增长的复利效应,生成每日累计现金流曲线。
- 逻辑细节 :代码中
-
年化 ROI (Return on Investment) :
ROI=(月净利润×12/总初始投资)×100%ROI = (月净利润 \times 12 / 总初始投资) \times 100\%ROI=(月净利润×12/总初始投资)×100%
C. 风险调整后收益指标 (专业量化核心)
这是该项目区别于普通计算器的最大亮点。它没有单纯看收益,而是衡量"承担一单位风险能获得多少超额收益"。
1. 夏普比率 (Sharpe Ratio)
衡量总波动风险 下的超额回报。
Sharpe=Rp−RfσpSharpe = \frac{R_p - R_f}{\sigma_p}Sharpe=σpRp−Rf
- 代码逻辑 :
- RpR_pRp (投资组合回报率):年化净利润率。
- RfR_fRf (无风险利率):默认 2.5% (国债水平)。
- σp\sigma_pσp (波动率):基于日收入波动率估算年化波动率 (σdaily×252\sigma_{daily} \times \sqrt{252}σdaily×252 )。
- 评估:夏普比率 > 1 通常被认为是好的,代码中根据行业基准进行了分级。
2. 索提诺比率 (Sortino Ratio)
衡量下行风险 (Downside Risk) 的指标,只惩罚亏损的波动,比夏普比率更精准。
Sortino=Rp−RfσdSortino = \frac{R_p - R_f}{\sigma_d}Sortino=σdRp−Rf
- 代码逻辑 :代码中简化处理,假设下行波动率是年化波动率的 70% (
downside_volatility = annual_volatility * 0.7)。
3. 特雷诺比率 (Treynor Ratio)
衡量系统性风险 (Beta) 下的超额回报。
Treynor=Rp−RfβTreynor = \frac{R_p - R_f}{\beta}Treynor=βRp−Rf
- 代码逻辑 :代码中通过
market_beta(市场贝塔) 和industry_beta(行业贝塔) 的平均值来估算项目贝塔系数,评估项目对市场波动的敏感度。
D. 财务健康度与运营效率
这部分将财务数据与行业基准进行对比。
- 成本结构分析 :
- 逻辑:计算租金、人工、物料占营收的比例。
- 基准对比 :代码中
IndustryBenchmarks定义了餐饮、便利店、奶茶店等行业的黄金比例。例如,餐饮业的食材成本占比基准是 30%-35%,如果计算结果超过此范围,系统会判定为"风险"。
- 运营效率 :
- 坪效 (Revenue / Area):每平方米产生的月营收。
- 人效 (Revenue / Staff):每人产生的月营收。
- 存货周转:评估库存变现速度。
3. 实现逻辑流程图解
用户输入 GUI
数据收集 collect_input_data
核心计算引擎 InvestmentCalculator
初始投资计算
收入与成本建模
回本与 ROI 计算
风险指标计算
财务健康度评估
运营效率分析
生成综合报告 generate_full_report
可视化 Charts
回本周期图
成本结构饼图
多维雷达图
投资仪表盘
GUI 结果展示
4. 代码
"""
投资计算评估应用
专业投资者指标分析工具
包含:夏普比率、索提诺比率、特雷诺比率、现金流质量、财务健康度、运营效率等
"""
import sys
import math
from datetime import datetime, timedelta
from typing import Dict, List, Tuple, Optional
import numpy as np
import pandas as pd
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QSplitter, QTabWidget, QLabel, QLineEdit, QPushButton, QComboBox,
QSpinBox, QDoubleSpinBox, QGroupBox, QScrollArea, QTableWidget,
QTableWidgetItem, QHeaderView, QProgressBar, QFrame, QGridLayout,
QTextEdit, QFileDialog, QMessageBox, QColorDialog, QCheckBox,
QListWidget, QCalendarWidget, QProgressDialog, QStatusBar,
QToolBar, QAction, QMenu, QMenuBar, QLCDNumber, QDial
)
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal, QDate, QSize
from PyQt5.QtGui import QFont, QIcon, QPalette, QColor, QPainter, QPen, QBrush, QPixmap
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5 import NavigationToolbar2QT
import matplotlib.pyplot as plt
from matplotlib.patches import Patch, Circle, Wedge
from matplotlib.figure import Figure
import matplotlib.patches as mpatches
from matplotlib.gridspec import GridSpec
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
from scipy import stats
# ============================================================================
# 行业基准数据库
# ============================================================================
class IndustryBenchmarks:
"""行业基准数据"""
BENCHMARKS = {
'餐饮': {
'name': '餐饮行业',
'break_even_optimal': (6, 12), # 优质回本周期(月)
'break_even_risky': 18, # 风险临界点(月)
'gross_margin': (60, 70), # 毛利率%
'net_margin': (5, 15), # 净利率%
'labor_cost_ratio': (18, 22), # 人力成本占比%
'rent_ratio': (10, 15), # 租金占比%
'material_ratio': (30, 35), # 食材成本占比%
'efficiency_per_sqm': (1000, 1500), # 坪效(元/㎡)
'turnover_rate': 2.0, # 翻台率
'repurchase_rate': 40, # 复购率%
'inventory_days': (15, 30), # 存货周转天数
'debt_ratio': 60, # 资产负债率%
'current_ratio': 1.5, # 流动比率
'sharpe_optimal': 1.2, # 优质夏普比率
'sortino_optimal': 1.5, # 优质索提诺比率
'treynor_optimal': 1.0, # 优质特雷诺比率
},
'零售': {
'name': '零售行业',
'break_even_optimal': (8, 18),
'break_even_risky': 24,
'gross_margin': (30, 40),
'net_margin': (3, 8),
'labor_cost_ratio': (20, 25),
'rent_ratio': (15, 20),
'material_ratio': (50, 60),
'efficiency_per_sqm': (800, 1200),
'turnover_rate': 4.0,
'repurchase_rate': 30,
'inventory_days': (30, 60),
'debt_ratio': 50,
'current_ratio': 1.5,
'sharpe_optimal': 1.0,
'sortino_optimal': 1.2,
'treynor_optimal': 0.8,
},
'服务': {
'name': '服务行业',
'break_even_optimal': (12, 24),
'break_even_risky': 30,
'gross_margin': (50, 70),
'net_margin': (10, 20),
'labor_cost_ratio': (25, 35),
'rent_ratio': (10, 18),
'material_ratio': (10, 20),
'efficiency_per_sqm': (800, 1500),
'turnover_rate': 1.0,
'repurchase_rate': 35,
'inventory_days': (7, 14),
'debt_ratio': 55,
'current_ratio': 1.8,
'sharpe_optimal': 1.1,
'sortino_optimal': 1.3,
'treynor_optimal': 0.9,
},
'便利店': {
'name': '便利店行业',
'break_even_optimal': (8, 12),
'break_even_risky': 18,
'gross_margin': (20, 30),
'net_margin': (2, 5),
'labor_cost_ratio': (15, 20),
'rent_ratio': (15, 25),
'material_ratio': (60, 70),
'efficiency_per_sqm': (2000, 3000),
'turnover_rate': 5.0,
'repurchase_rate': 50,
'inventory_days': (7, 14),
'debt_ratio': 45,
'current_ratio': 1.2,
'sharpe_optimal': 0.8,
'sortino_optimal': 1.0,
'treynor_optimal': 0.6,
},
'水果店': {
'name': '水果零售',
'break_even_optimal': (6, 10),
'break_even_risky': 15,
'gross_margin': (50, 65),
'net_margin': (8, 15),
'labor_cost_ratio': (15, 22),
'rent_ratio': (12, 18),
'material_ratio': (40, 50),
'efficiency_per_sqm': (1500, 2500),
'turnover_rate': 3.0,
'repurchase_rate': 45,
'inventory_days': (3, 7),
'debt_ratio': 40,
'current_ratio': 1.5,
'sharpe_optimal': 1.3,
'sortino_optimal': 1.6,
'treynor_optimal': 1.0,
},
'奶茶店': {
'name': '奶茶饮品店',
'break_even_optimal': (12, 18),
'break_even_risky': 24,
'gross_margin': (65, 75),
'net_margin': (10, 18),
'labor_cost_ratio': (20, 28),
'rent_ratio': (15, 22),
'material_ratio': (25, 35),
'efficiency_per_sqm': (3000, 5000),
'turnover_rate': 4.0,
'repurchase_rate': 55,
'inventory_days': (2, 5),
'debt_ratio': 50,
'current_ratio': 1.4,
'sharpe_optimal': 1.4,
'sortino_optimal': 1.7,
'treynor_optimal': 1.1,
},
'酒店': {
'name': '酒店住宿',
'break_even_optimal': (18, 36),
'break_even_risky': 60,
'gross_margin': (55, 70),
'net_margin': (8, 20),
'labor_cost_ratio': (25, 35),
'rent_ratio': (20, 35),
'material_ratio': (8, 15),
'efficiency_per_sqm': (500, 1000),
'turnover_rate': 0.7,
'repurchase_rate': 25,
'inventory_days': (1, 3),
'debt_ratio': 65,
'current_ratio': 1.2,
'sharpe_optimal': 0.9,
'sortino_optimal': 1.1,
'treynor_optimal': 0.7,
},
'自定义': {
'name': '自定义行业',
'break_even_optimal': (12, 24),
'break_even_risky': 36,
'gross_margin': (40, 60),
'net_margin': (5, 15),
'labor_cost_ratio': (20, 30),
'rent_ratio': (15, 25),
'material_ratio': (30, 50),
'efficiency_per_sqm': (800, 1500),
'turnover_rate': 2.0,
'repurchase_rate': 35,
'inventory_days': (15, 45),
'debt_ratio': 55,
'current_ratio': 1.5,
'sharpe_optimal': 1.0,
'sortino_optimal': 1.2,
'treynor_optimal': 0.8,
}
}
@classmethod
def get_benchmark(cls, industry: str) -> Dict:
"""获取行业基准"""
return cls.BENCHMARKS.get(industry, cls.BENCHMARKS['自定义'])
# ============================================================================
# 投资计算引擎
# ============================================================================
class InvestmentCalculator:
"""投资计算引擎"""
def __init__(self):
self.data = {}
self.results = {}
def set_data(self, data: Dict):
"""设置输入数据"""
self.data = data
def calculate_initial_investment(self) -> Dict:
"""计算初始投资"""
transfer_fee = self.data.get('transfer_fee', 0) # 转让费
deposit_months = self.data.get('deposit_months', 1) # 押金月数
advance_months = self.data.get('advance_months', 2) # 预付月数
monthly_rent = self.data.get('monthly_rent', 0) # 月租金
initial_rent = monthly_rent * (deposit_months + advance_months) # 首期租金
renovation_cost = self.data.get('renovation_cost', 0) # 装修费
equipment_cost = self.data.get('equipment_cost', 0) # 设备采购
other_costs = self.data.get('other_costs', 0) # 其他费用
total_investment = (
transfer_fee + initial_rent +
renovation_cost + equipment_cost + other_costs
)
return {
'transfer_fee': transfer_fee,
'initial_rent': initial_rent,
'renovation_cost': renovation_cost,
'equipment_cost': equipment_cost,
'other_costs': other_costs,
'total_investment': total_investment,
'investment_breakdown': {
'转让费': transfer_fee,
'首期租金': initial_rent,
'装修费': renovation_cost,
'设备费': equipment_cost,
'其他费用': other_costs
}
}
def calculate_revenue(self) -> Dict:
"""计算收入"""
daily_revenue_min = self.data.get('daily_revenue_min', 0)
daily_revenue_max = self.data.get('daily_revenue_max', 0)
operating_days = self.data.get('operating_days', 30) # 月运营天数
monthly_revenue_min = daily_revenue_min * operating_days
monthly_revenue_max = daily_revenue_max * operating_days
monthly_revenue_avg = (daily_revenue_min + daily_revenue_max) / 2 * operating_days
annual_revenue = monthly_revenue_avg * 12
return {
'daily_revenue_min': daily_revenue_min,
'daily_revenue_max': daily_revenue_max,
'daily_revenue_avg': (daily_revenue_min + daily_revenue_max) / 2,
'monthly_revenue_min': monthly_revenue_min,
'monthly_revenue_max': monthly_revenue_max,
'monthly_revenue_avg': monthly_revenue_avg,
'annual_revenue': annual_revenue
}
def calculate_costs(self) -> Dict:
"""计算成本 - 直接使用用户输入的实际成本数据"""
revenue_data = self.calculate_revenue()
monthly_revenue = revenue_data['monthly_revenue_avg']
# 直接使用用户输入的实际金额
monthly_rent = self.data.get('cost_rent', 2800) # 月租金
monthly_labor = self.data.get('cost_labor', 4000) # 人力成本
monthly_material = self.data.get('cost_material', 6000) # 食材/进货成本
monthly_utilities = self.data.get('cost_utilities', 600) # 水电
monthly_other = self.data.get('cost_other', 1000) # 其他
# 实际总成本(用户输入是多少就是多少)
monthly_total_cost = (
monthly_rent + monthly_labor + monthly_material +
monthly_utilities + monthly_other
)
# 实际净利润 = 营业额 - 所有成本
monthly_profit = monthly_revenue - monthly_total_cost
# 实际利润率
profit_rate = monthly_profit / monthly_revenue if monthly_revenue > 0 else 0
# 目标利润率(仅作为参考信息显示)
target_profit_rate = self.data.get('profit_rate', 0.15)
target_profit = monthly_revenue * target_profit_rate
# 各项成本占比(相对于总收入)
rent_ratio = monthly_rent / monthly_revenue if monthly_revenue > 0 else 0
labor_ratio = monthly_labor / monthly_revenue if monthly_revenue > 0 else 0
material_ratio = monthly_material / monthly_revenue if monthly_revenue > 0 else 0
utilities_ratio = monthly_utilities / monthly_revenue if monthly_revenue > 0 else 0
other_ratio = monthly_other / monthly_revenue if monthly_revenue > 0 else 0
return {
'profit_rate': profit_rate,
'rent_ratio': rent_ratio,
'labor_ratio': labor_ratio,
'material_ratio': material_ratio,
'utilities_ratio': utilities_ratio,
'other_ratio': other_ratio,
'monthly_profit': monthly_profit,
'monthly_rent': monthly_rent,
'monthly_labor': monthly_labor,
'monthly_material': monthly_material,
'monthly_utilities': monthly_utilities,
'monthly_other': monthly_other,
'monthly_total_cost': monthly_total_cost,
'target_profit_rate': target_profit_rate,
'target_profit': target_profit,
'profit_diff': monthly_profit - target_profit, # 与目标的差额
'cost_breakdown': {
'租金': monthly_rent,
'人力成本': monthly_labor,
'食材/进货': monthly_material,
'水电费': monthly_utilities,
'其他成本': monthly_other
}
}
def calculate_break_even(self) -> Dict:
"""计算回本周期"""
investment = self.calculate_initial_investment()
costs = self.calculate_costs()
total_investment = investment['total_investment']
monthly_profit = costs['monthly_profit']
daily_profit = monthly_profit / 30
# 精确回本周期计算
if daily_profit > 0:
break_even_days = total_investment / daily_profit
break_even_months = break_even_days / 30
else:
break_even_days = float('inf')
break_even_months = float('inf')
# ROI计算
annual_profit = monthly_profit * 12
roi = (annual_profit / total_investment) * 100 if total_investment > 0 else 0
# 生成每日累计现金流数据
days_list = list(range(int(break_even_days) + 60)) # 回本后延长60天
daily_cashflow = []
cumulative = -total_investment
for day in days_list:
daily_profit_current = daily_profit * (1 + self.data.get('growth_rate', 0) / 365) ** day
cumulative += daily_profit_current
daily_cashflow.append({
'day': day,
'daily_profit': daily_profit_current,
'cumulative_cashflow': cumulative
})
return {
'total_investment': total_investment,
'monthly_profit': monthly_profit,
'daily_profit': daily_profit,
'break_even_days': break_even_days,
'break_even_months': break_even_months,
'roi': roi,
'annual_profit': annual_profit,
'payback_quality': self._assess_payback_quality(break_even_months),
'daily_cashflow': daily_cashflow
}
def _assess_payback_quality(self, months: float) -> Dict:
"""评估回本周期质量"""
industry = self.data.get('industry', '自定义')
benchmark = IndustryBenchmarks.get_benchmark(industry)
optimal_min, optimal_max = benchmark['break_even_optimal']
risky = benchmark['break_even_risky']
if months <= optimal_min:
quality = '优秀'
color = 'green'
score = 100
suggestion = '回本周期非常优秀,属于行业标杆水平'
elif months <= optimal_max:
quality = '良好'
color = 'blue'
score = 80
suggestion = '回本周期在优质区间内'
elif months <= risky:
quality = '一般'
color = 'orange'
score = 60
suggestion = '回本周期可接受,但需关注成本控制'
else:
quality = '风险'
color = 'red'
score = 30
suggestion = '回本周期过长,建议谨慎投资'
return {
'quality': quality,
'color': color,
'score': score,
'suggestion': suggestion,
'optimal_range': f'{optimal_min}-{optimal_max}个月',
'actual': f'{months:.1f}个月'
}
def calculate_risk_adjusted_metrics(self) -> Dict:
"""计算风险调整后收益指标"""
break_even = self.calculate_break_even()
annual_profit = break_even['annual_profit']
total_investment = break_even['total_investment']
# 年化收益率
expected_return = annual_profit / total_investment if total_investment > 0 else 0
# 无风险利率(国债收益率,约2.5%)
risk_free_rate = self.data.get('risk_free_rate', 0.025)
# 波动率估算(基于日收入波动)
daily_revenue_volatility = self.data.get('daily_revenue_volatility', 0.1)
# 年化波动率
annual_volatility = daily_revenue_volatility * math.sqrt(252)
# 下行波动率(仅考虑负收益)
downside_volatility = annual_volatility * 0.7 # 简化估算
# 贝塔系数
market_beta = self.data.get('market_beta', 1.0)
industry_beta = self.data.get('industry_beta', 1.2)
beta = (market_beta + industry_beta) / 2
# 夏普比率 = (预期收益 - 无风险利率) / 波动率
if annual_volatility > 0:
sharpe_ratio = (expected_return - risk_free_rate) / annual_volatility
else:
sharpe_ratio = 0
# 索提诺比率 = (预期收益 - 无风险利率) / 下行波动率
if downside_volatility > 0:
sortino_ratio = (expected_return - risk_free_rate) / downside_volatility
else:
sortino_ratio = 0
# 特雷诺比率 = (预期收益 - 无风险利率) / 贝塔
if beta > 0:
treynor_ratio = (expected_return - risk_free_rate) / beta
else:
treynor_ratio = 0
# 评估指标质量
industry = self.data.get('industry', '自定义')
benchmark = IndustryBenchmarks.get_benchmark(industry)
return {
'expected_return': expected_return,
'risk_free_rate': risk_free_rate,
'annual_volatility': annual_volatility,
'downside_volatility': downside_volatility,
'beta': beta,
'sharpe_ratio': sharpe_ratio,
'sortino_ratio': sortino_ratio,
'treynor_ratio': treynor_ratio,
'sharpe_assessment': self._assess_sharpe(sharpe_ratio, benchmark['sharpe_optimal']),
'sortino_assessment': self._assess_sortino(sortino_ratio, benchmark['sortino_optimal']),
'treynor_assessment': self._assess_treynor(treynor_ratio, benchmark['treynor_optimal'])
}
def _assess_sharpe(self, sharpe: float, optimal: float) -> Dict:
"""评估夏普比率"""
if sharpe >= optimal * 1.5:
return {'level': '优秀', 'color': 'green', 'score': 100,
'suggestion': '风险收益比极佳,是优质投资标的'}
elif sharpe >= optimal:
return {'level': '良好', 'color': 'blue', 'score': 80,
'suggestion': '风险收益比良好,符合专业标准'}
elif sharpe >= optimal * 0.7:
return {'level': '一般', 'color': 'orange', 'score': 60,
'suggestion': '风险收益比处于可接受范围'}
else:
return {'level': '风险', 'color': 'red', 'score': 40,
'suggestion': '风险收益比不佳,建议谨慎'}
def _assess_sortino(self, sortino: float, optimal: float) -> Dict:
"""评估索提诺比率"""
if sortino >= optimal * 1.5:
return {'level': '优秀', 'color': 'green', 'score': 100,
'suggestion': '下行风险控制极佳'}
elif sortino >= optimal:
return {'level': '良好', 'color': 'blue', 'score': 80,
'suggestion': '下行风险控制良好'}
elif sortino >= optimal * 0.7:
return {'level': '一般', 'color': 'orange', 'score': 60,
'suggestion': '下行风险控制一般'}
else:
return {'level': '风险', 'color': 'red', 'score': 40,
'suggestion': '下行风险较大,需关注'}
def _assess_treynor(self, treynor: float, optimal: float) -> Dict:
"""评估特雷诺比率"""
if treynor >= optimal * 1.5:
return {'level': '优秀', 'color': 'green', 'score': 100,
'suggestion': '系统性风险管理优秀'}
elif treynor >= optimal:
return {'level': '良好', 'color': 'blue', 'score': 80,
'suggestion': '系统性风险管理良好'}
elif treynor >= optimal * 0.7:
return {'level': '一般', 'color': 'orange', 'score': 60,
'suggestion': '系统性风险管理一般'}
else:
return {'level': '风险', 'color': 'red', 'score': 40,
'suggestion': '系统性风险较高,需谨慎'}
def calculate_cashflow_quality(self) -> Dict:
"""计算现金流质量指标"""
costs = self.calculate_costs()
revenue = self.calculate_revenue()
monthly_revenue = revenue['monthly_revenue_avg']
monthly_profit = costs['monthly_profit']
monthly_cost = costs['monthly_total_cost']
# 经营现金流(简化:利润+非现金成本)
depreciation = self.data.get('monthly_depreciation', 0)
operating_cashflow = monthly_profit + depreciation
# 自由现金流 = 经营现金流 - 资本支出
monthly_capex = self.data.get('monthly_capex', 0)
free_cashflow = operating_cashflow - monthly_capex
# 现金流质量比率 = 经营现金流 / 净利润
if monthly_profit > 0:
cf_to_income_ratio = operating_cashflow / monthly_profit
else:
cf_to_income_ratio = 0
# 自由现金流比率 = 自由现金流 / 净利润
if monthly_profit > 0:
fcf_to_income_ratio = free_cashflow / monthly_profit
else:
fcf_to_income_ratio = 0
# 现金转换周期
cash_conversion_days = 30 * (1 - cf_to_income_ratio) if cf_to_income_ratio < 1 else 0
# 现金流稳定性(基于收入波动估算)
revenue_volatility = self.data.get('daily_revenue_volatility', 0.1)
return {
'monthly_revenue': monthly_revenue,
'monthly_profit': monthly_profit,
'operating_cashflow': operating_cashflow,
'free_cashflow': free_cashflow,
'cf_to_income_ratio': cf_to_income_ratio,
'fcf_to_income_ratio': fcf_to_income_ratio,
'cash_conversion_days': cash_conversion_days,
'revenue_volatility': revenue_volatility,
'quality_assessment': self._assess_cf_quality(cf_to_income_ratio, fcf_to_income_ratio),
'stability_assessment': self._assess_cf_stability(revenue_volatility)
}
def _assess_cf_quality(self, cf_ratio: float, fcf_ratio: float) -> Dict:
"""评估现金流质量"""
if cf_ratio >= 1.2 and fcf_ratio >= 0.8:
level = '优秀'
color = 'green'
score = 100
suggestion = '现金流质量极佳,利润转化为现金能力强'
elif cf_ratio >= 1.0 and fcf_ratio >= 0.5:
level = '良好'
color = 'blue'
score = 80
suggestion = '现金流质量良好'
elif cf_ratio >= 0.8:
level = '一般'
color = 'orange'
score = 60
suggestion = '现金流质量一般,需关注应收款项'
else:
level = '风险'
color = 'red'
score = 40
suggestion = '现金流质量差,存在现金流断裂风险'
return {
'level': level,
'color': color,
'score': score,
'suggestion': suggestion,
'cf_ratio': cf_ratio,
'fcf_ratio': fcf_ratio
}
def _assess_cf_stability(self, volatility: float) -> Dict:
"""评估现金流稳定性"""
if volatility <= 0.05:
level = '优秀'
color = 'green'
score = 100
elif volatility <= 0.1:
level = '良好'
color = 'blue'
score = 80
elif volatility <= 0.2:
level = '一般'
color = 'orange'
score = 60
else:
level = '风险'
color = 'red'
score = 40
return {
'level': level,
'color': color,
'score': score,
'volatility': volatility
}
def calculate_financial_health(self) -> Dict:
"""计算财务健康度指标"""
costs = self.calculate_costs()
revenue = self.calculate_revenue()
monthly_revenue = revenue['monthly_revenue_avg']
monthly_cost = costs['monthly_total_cost']
monthly_profit = costs['monthly_profit']
monthly_material = costs['monthly_material']
# 毛利率 = (收入 - 物料成本) / 收入
gross_margin = (monthly_revenue - monthly_material) / monthly_revenue if monthly_revenue > 0 else 0
# 净利率
net_margin = monthly_profit / monthly_revenue if monthly_revenue > 0 else 0
# 资产负债率
investment = self.calculate_initial_investment()
total_assets = investment['total_investment']
total_liabilities = self.data.get('total_liabilities', 0)
debt_ratio = (total_liabilities / total_assets * 100) if total_assets > 0 else 0
# 流动比率
current_assets = self.data.get('current_assets', monthly_profit * 3)
current_liabilities = self.data.get('current_liabilities', monthly_cost * 2)
current_ratio = current_assets / current_liabilities if current_liabilities > 0 else 2.0
# 速动比率
quick_assets = current_assets - self.data.get('inventory', 0)
quick_ratio = quick_assets / current_liabilities if current_liabilities > 0 else 1.5
# 成本结构分析 - 使用实际占比
industry = self.data.get('industry', '自定义')
benchmark = IndustryBenchmarks.get_benchmark(industry)
# 使用计算后的实际占比
rent_ratio_pct = costs['rent_ratio'] * 100
labor_ratio_pct = costs['labor_ratio'] * 100
material_ratio_pct = costs['material_ratio'] * 100
return {
'gross_margin': gross_margin * 100,
'net_margin': net_margin * 100,
'debt_ratio': debt_ratio,
'current_ratio': current_ratio,
'quick_ratio': quick_ratio,
'cost_structure': {
'rent_ratio': rent_ratio_pct,
'labor_ratio': labor_ratio_pct,
'material_ratio': material_ratio_pct,
'utilities_ratio': costs['utilities_ratio'] * 100,
'other_ratio': costs['other_ratio'] * 100
},
'gross_margin_assessment': self._assess_gross_margin(gross_margin * 100, benchmark['gross_margin']),
'net_margin_assessment': self._assess_net_margin(net_margin * 100, benchmark['net_margin']),
'debt_ratio_assessment': self._assess_debt_ratio(debt_ratio, benchmark['debt_ratio']),
'current_ratio_assessment': self._assess_current_ratio(current_ratio, benchmark['current_ratio']),
'cost_structure_assessment': self._assess_cost_structure(rents=rent_ratio_pct, labor=labor_ratio_pct,
material=material_ratio_pct, benchmark=benchmark)
}
def _assess_gross_margin(self, margin: float, benchmark: Tuple) -> Dict:
"""评估毛利率"""
if benchmark[0] <= margin <= benchmark[1]:
return {'level': '良好', 'color': 'green', 'score': 100, 'value': margin}
elif margin < benchmark[0]:
diff = benchmark[0] - margin
if diff <= 5:
return {'level': '一般', 'color': 'orange', 'score': 70, 'value': margin}
else:
return {'level': '风险', 'color': 'red', 'score': 50, 'value': margin}
else:
return {'level': '优秀', 'color': 'blue', 'score': 110, 'value': margin}
def _assess_net_margin(self, margin: float, benchmark: Tuple) -> Dict:
"""评估净利率"""
if benchmark[0] <= margin <= benchmark[1]:
return {'level': '良好', 'color': 'green', 'score': 100, 'value': margin}
elif margin < benchmark[0]:
if margin >= benchmark[0] * 0.7:
return {'level': '一般', 'color': 'orange', 'score': 70, 'value': margin}
else:
return {'level': '风险', 'color': 'red', 'score': 50, 'value': margin}
else:
return {'level': '优秀', 'color': 'blue', 'score': 110, 'value': margin}
def _assess_debt_ratio(self, ratio: float, benchmark: float) -> Dict:
"""评估资产负债率"""
if ratio <= benchmark:
return {'level': '良好', 'color': 'green', 'score': 100, 'value': ratio}
elif ratio <= benchmark * 1.2:
return {'level': '一般', 'color': 'orange', 'score': 70, 'value': ratio}
else:
return {'level': '风险', 'color': 'red', 'score': 50, 'value': ratio}
def _assess_current_ratio(self, ratio: float, benchmark: float) -> Dict:
"""评估流动比率"""
if ratio >= benchmark:
return {'level': '良好', 'color': 'green', 'score': 100, 'value': ratio}
elif ratio >= benchmark * 0.8:
return {'level': '一般', 'color': 'orange', 'score': 70, 'value': ratio}
else:
return {'level': '风险', 'color': 'red', 'score': 50, 'value': ratio}
def _assess_cost_structure(self, rents: float = None, labor: float = None,
material: float = None, benchmark: Dict = None) -> Dict:
"""评估成本结构 - 基于收入占比"""
if benchmark is None:
benchmark = IndustryBenchmarks.get_benchmark(self.data.get('industry', '自定义'))
issues = []
warnings = []
score = 100
# 租金占比(相对于收入)
rent_benchmark = benchmark.get('rent_ratio', (10, 20))
if rents is not None:
if rents > rent_benchmark[1] * 1.5:
issues.append(f"租金占比({rents:.1f}%)远超行业上限({rent_benchmark[1]}%)")
score -= 25
elif rents > rent_benchmark[1]:
warnings.append(f"租金占比({rents:.1f}%)略高于行业上限({rent_benchmark[1]}%)")
score -= 10
# 人力成本占比(相对于收入)
labor_benchmark = benchmark.get('labor_cost_ratio', (18, 25))
if labor is not None:
if labor > labor_benchmark[1] * 1.5:
issues.append(f"人力成本占比({labor:.1f}%)远超行业上限({labor_benchmark[1]}%)")
score -= 25
elif labor > labor_benchmark[1]:
warnings.append(f"人力成本占比({labor:.1f}%)略高于行业上限({labor_benchmark[1]}%)")
score -= 10
# 物料占比(相对于收入)
material_benchmark = benchmark.get('material_ratio', (30, 35))
if material is not None:
if material > material_benchmark[1] * 1.5:
issues.append(f"物料占比({material:.1f}%)远超行业上限({material_benchmark[1]}%)")
score -= 25
elif material > material_benchmark[1]:
warnings.append(f"物料占比({material:.1f}%)略高于行业上限({material_benchmark[1]}%)")
score -= 10
all_issues = issues + warnings
if not all_issues:
level = '良好'
color = 'green'
suggestion = '成本结构符合行业黄金比例'
elif len(all_issues) == 1:
level = '一般'
color = 'orange'
suggestion = all_issues[0]
else:
level = '风险'
color = 'red'
suggestion = '存在多项成本异常,需重点关注'
return {
'level': level,
'color': color,
'score': max(0, score),
'issues': all_issues,
'suggestion': suggestion
}
def calculate_operational_efficiency(self) -> Dict:
"""计算运营效率指标"""
revenue = self.calculate_revenue()
costs = self.calculate_costs()
monthly_revenue = revenue['monthly_revenue_avg']
area = self.data.get('area', 100) # 经营面积(平方米)
staff_count = self.data.get('staff_count', 2) # 员工数量
seats = self.data.get('seats', 20) # 座位数
daily_customers = self.data.get('daily_customers', 50) # 日客流量
# 坪效(元/平方米/月)
efficiency_per_sqm = monthly_revenue / area if area > 0 else 0
# 人效(元/人/月)
efficiency_per_person = monthly_revenue / staff_count if staff_count > 0 else 0
# 客单价
operating_days = self.data.get('operating_days', 30)
avg_customer_value = monthly_revenue / (daily_customers * operating_days) if daily_customers > 0 else 0
# 翻台率(餐饮)
turnover_rate = (daily_customers * operating_days / 30) / seats if seats > 0 else 0
# 存货周转率
monthly_material = costs['monthly_material']
avg_inventory = self.data.get('avg_inventory', monthly_material * 5)
inventory_turnover = monthly_material / avg_inventory if avg_inventory > 0 else 0
inventory_days = 30 / inventory_turnover if inventory_turnover > 0 else 30
industry = self.data.get('industry', '自定义')
benchmark = IndustryBenchmarks.get_benchmark(industry)
return {
'efficiency_per_sqm': efficiency_per_sqm,
'efficiency_per_person': efficiency_per_person,
'avg_customer_value': avg_customer_value,
'turnover_rate': turnover_rate,
'inventory_turnover': inventory_turnover,
'inventory_days': inventory_days,
'sqm_efficiency_assessment': self._assess_sqm_efficiency(efficiency_per_sqm, benchmark['efficiency_per_sqm']),
'person_efficiency_assessment': self._assess_person_efficiency(efficiency_per_person, benchmark),
'turnover_assessment': self._assess_turnover(turnover_rate, benchmark['turnover_rate']),
'inventory_assessment': self._assess_inventory(inventory_days, benchmark['inventory_days'])
}
def _assess_sqm_efficiency(self, value: float, benchmark: Tuple) -> Dict:
"""评估坪效"""
if value >= benchmark[1]:
return {'level': '优秀', 'color': 'green', 'score': 100, 'value': value, 'benchmark': benchmark}
elif value >= benchmark[0]:
return {'level': '良好', 'color': 'blue', 'score': 80, 'value': value, 'benchmark': benchmark}
elif value >= benchmark[0] * 0.8:
return {'level': '一般', 'color': 'orange', 'score': 60, 'value': value, 'benchmark': benchmark}
else:
return {'level': '风险', 'color': 'red', 'score': 40, 'value': value, 'benchmark': benchmark}
def _assess_person_efficiency(self, value: float, benchmark: Dict) -> Dict:
"""评估人效"""
# 简化估算人效基准
baseline = 150000 # 15万/人/年
if value * 12 >= baseline * 1.2:
return {'level': '优秀', 'color': 'green', 'score': 100, 'value': value}
elif value * 12 >= baseline:
return {'level': '良好', 'color': 'blue', 'score': 80, 'value': value}
elif value * 12 >= baseline * 0.8:
return {'level': '一般', 'color': 'orange', 'score': 60, 'value': value}
else:
return {'level': '风险', 'color': 'red', 'score': 40, 'value': value}
def _assess_turnover(self, rate: float, benchmark: float) -> Dict:
"""评估翻台率"""
if rate >= benchmark * 1.2:
return {'level': '优秀', 'color': 'green', 'score': 100, 'value': rate, 'benchmark': benchmark}
elif rate >= benchmark:
return {'level': '良好', 'color': 'blue', 'score': 80, 'value': rate, 'benchmark': benchmark}
elif rate >= benchmark * 0.7:
return {'level': '一般', 'color': 'orange', 'score': 60, 'value': rate, 'benchmark': benchmark}
else:
return {'level': '风险', 'color': 'red', 'score': 40, 'value': rate, 'benchmark': benchmark}
def _assess_inventory(self, days: float, benchmark: Tuple) -> Dict:
"""评估存货周转"""
if benchmark[0] <= days <= benchmark[1]:
return {'level': '良好', 'color': 'green', 'score': 100, 'value': days, 'benchmark': benchmark}
elif days < benchmark[0]:
return {'level': '优秀', 'color': 'blue', 'score': 110, 'value': days, 'benchmark': benchmark}
elif days <= benchmark[1] * 1.3:
return {'level': '一般', 'color': 'orange', 'score': 60, 'value': days, 'benchmark': benchmark}
else:
return {'level': '风险', 'color': 'red', 'score': 40, 'value': days, 'benchmark': benchmark}
def calculate_portfolio_metrics(self) -> Dict:
"""计算投资组合管理指标"""
risk_metrics = self.calculate_risk_adjusted_metrics()
cashflow = self.calculate_cashflow_quality()
financial = self.calculate_financial_health()
operation = self.calculate_operational_efficiency()
# 投资分散化建议
correlation_suggestion = self._assess_correlation()
# 综合评分
overall_score = (
risk_metrics['sharpe_assessment']['score'] * 0.25 +
cashflow['quality_assessment']['score'] * 0.25 +
financial['cost_structure_assessment']['score'] * 0.25 +
operation['sqm_efficiency_assessment']['score'] * 0.25
)
# 投资评级
if overall_score >= 90:
rating = 'A+'
rating_color = 'green'
recommendation = '强烈推荐'
elif overall_score >= 80:
rating = 'A'
rating_color = 'green'
recommendation = '推荐投资'
elif overall_score >= 70:
rating = 'B+'
rating_color = 'blue'
recommendation = '可以投资'
elif overall_score >= 60:
rating = 'B'
rating_color = 'blue'
recommendation = '谨慎投资'
elif overall_score >= 50:
rating = 'C'
rating_color = 'orange'
recommendation = '不建议投资'
else:
rating = 'D'
rating_color = 'red'
recommendation = '不建议投资'
return {
'overall_score': overall_score,
'rating': rating,
'rating_color': rating_color,
'recommendation': recommendation,
'correlation_suggestion': correlation_suggestion,
'dimension_scores': {
'risk_adjusted': risk_metrics['sharpe_assessment']['score'],
'cashflow_quality': cashflow['quality_assessment']['score'],
'financial_health': financial['cost_structure_assessment']['score'],
'operational_efficiency': operation['sqm_efficiency_assessment']['score']
}
}
def _assess_correlation(self) -> Dict:
"""评估资产相关性"""
industry = self.data.get('industry', '自定义')
# 不同行业的相关性建议
suggestions = {
'餐饮': '建议与零售、服务业组合,降低行业集中度风险',
'零售': '建议与餐饮、娱乐业组合,分散消费周期性风险',
'服务': '建议与餐饮、零售业组合,平衡抗周期能力',
'便利店': '建议与餐饮、社区服务业组合,提高协同效应',
'水果店': '建议与便利店、零售业组合,共享供应链优势',
'奶茶店': '建议与餐饮、娱乐业组合,吸引相同客群',
'酒店': '建议与旅游、娱乐业组合,对冲季节性风险',
'自定义': '建议进行多元化配置,降低单一项目风险'
}
return {
'suggestion': suggestions.get(industry, suggestions['自定义']),
'ideal_portfolio_size': 5,
'max_concentration': 30 # 单项目最大占比%
}
def generate_full_report(self) -> Dict:
"""生成完整评估报告"""
return {
'initial_investment': self.calculate_initial_investment(),
'revenue': self.calculate_revenue(),
'costs': self.calculate_costs(),
'break_even': self.calculate_break_even(),
'risk_adjusted': self.calculate_risk_adjusted_metrics(),
'cashflow_quality': self.calculate_cashflow_quality(),
'financial_health': self.calculate_financial_health(),
'operational_efficiency': self.calculate_operational_efficiency(),
'portfolio_metrics': self.calculate_portfolio_metrics()
}
# ============================================================================
# 可视化图表类
# ============================================================================
class InvestmentCharts:
"""投资可视化图表"""
def __init__(self, data: Dict, results: Dict):
self.data = data
self.results = results
def create_break_even_chart(self, parent=None) -> Figure:
"""创建回本周期图表"""
fig, ax = plt.subplots(figsize=(12, 6))
daily_cashflow = self.results['break_even']['daily_cashflow']
days = [item['day'] for item in daily_cashflow]
cumulative = [item['cumulative_cashflow'] for item in daily_cashflow]
ax.plot(days, cumulative, 'b-', linewidth=2, label='累计现金流')
ax.axhline(y=0, color='r', linestyle='--', linewidth=1, label='盈亏平衡线')
# 标记回本点
break_even_days = self.results['break_even']['break_even_days']
if break_even_days < len(days):
ax.axvline(x=break_even_days, color='g', linestyle=':', linewidth=2,
label=f'回本点({break_even_days:.0f}天)')
ax.scatter([break_even_days], [0], color='g', s=100, zorder=5)
ax.fill_between(days, cumulative, 0, where=[c >= 0 for c in cumulative],
alpha=0.3, color='green', label='盈利区间')
ax.fill_between(days, cumulative, 0, where=[c < 0 for c in cumulative],
alpha=0.3, color='red', label='亏损区间')
ax.set_xlabel('天数', fontsize=12)
ax.set_ylabel('现金流(元)', fontsize=12)
ax.set_title('回本周期分析', fontsize=14, fontweight='bold')
ax.legend(loc='upper left')
ax.grid(True, alpha=0.3)
plt.tight_layout()
return fig
def create_cost_structure_chart(self, parent=None) -> Figure:
"""创建成本结构图表"""
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 饼图 - 成本占比
costs = self.results['costs']['cost_breakdown']
labels = list(costs.keys())
sizes = list(costs.values())
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#ff99cc']
axes[0].pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90,
colors=colors, explode=[0.05]*len(sizes))
axes[0].set_title('成本结构占比', fontsize=14, fontweight='bold')
# 柱状图 - 月度成本明细
x = range(len(labels))
bars = axes[1].bar(x, sizes, color=colors)
axes[1].set_xticks(x)
axes[1].set_xticklabels(labels, rotation=45, ha='right')
axes[1].set_ylabel('金额(元/月)', fontsize=12)
axes[1].set_title('月度成本明细', fontsize=14, fontweight='bold')
# 添加数值标签
for bar, val in zip(bars, sizes):
axes[1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 50,
f'{val:.0f}', ha='center', va='bottom', fontsize=9)
plt.tight_layout()
return fig
def create_monthly_projection_chart(self, parent=None) -> Figure:
"""创建月度预测图表"""
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
revenue = self.results['revenue']
costs = self.results['costs']
months = range(1, 37) # 36个月预测
# 月度收入
monthly_revenue = [revenue['monthly_revenue_avg']] * 36
axes[0, 0].plot(months, monthly_revenue, 'g-', linewidth=2, marker='o', markersize=3)
axes[0, 0].fill_between(months,
[revenue['monthly_revenue_min']] * 36,
[revenue['monthly_revenue_max']] * 36,
alpha=0.3, color='green')
axes[0, 0].set_title('月度收入预测', fontsize=12, fontweight='bold')
axes[0, 0].set_xlabel('月份')
axes[0, 0].set_ylabel('收入(元)')
axes[0, 0].grid(True, alpha=0.3)
# 月度利润
growth_rate = self.data.get('growth_rate', 0)
monthly_profit = [costs['monthly_profit'] * (1 + growth_rate/12)**m for m in months]
axes[0, 1].plot(months, monthly_profit, 'b-', linewidth=2, marker='s', markersize=3)
axes[0, 1].fill_between(months, 0, monthly_profit, alpha=0.3, color='blue')
axes[0, 1].set_title('月度利润预测', fontsize=12, fontweight='bold')
axes[0, 1].set_xlabel('月份')
axes[0, 1].set_ylabel('利润(元)')
axes[0, 1].grid(True, alpha=0.3)
# 累计现金流
investment = self.results['initial_investment']['total_investment']
cumulative = [-investment]
for m in months:
monthly_net = revenue['monthly_revenue_avg'] * (1 + growth_rate/12)**m - costs['monthly_total_cost']
cumulative.append(cumulative[-1] + monthly_net)
axes[1, 0].plot(range(0, 37), cumulative, 'purple', linewidth=2, marker='^', markersize=3)
axes[1, 0].axhline(y=0, color='r', linestyle='--')
axes[1, 0].fill_between(range(0, 37), 0, cumulative, where=[c >= 0 for c in cumulative],
alpha=0.3, color='green')
axes[1, 0].fill_between(range(0, 37), 0, cumulative, where=[c < 0 for c in cumulative],
alpha=0.3, color='red')
axes[1, 0].set_title('累计现金流变化', fontsize=12, fontweight='bold')
axes[1, 0].set_xlabel('月份')
axes[1, 0].set_ylabel('现金流(元)')
axes[1, 0].grid(True, alpha=0.3)
# 3年总回报
total_return = sum(monthly_profit) - investment
roi_3y = (total_return / investment) * 100 if investment > 0 else 0
categories = ['初始投资', '3年累计利润', '3年总回报']
values = [investment, sum(monthly_profit), total_return]
colors_bar = ['#ff6b6b', '#4ecdc4', '#45b7d1']
bars = axes[1, 1].bar(categories, values, color=colors_bar)
axes[1, 1].set_title(f'3年投资回报 (ROI: {roi_3y:.1f}%)', fontsize=12, fontweight='bold')
axes[1, 1].set_ylabel('金额(元)')
for bar, val in zip(bars, values):
axes[1, 1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1000,
f'{val:,.0f}', ha='center', va='bottom', fontsize=9)
plt.tight_layout()
return fig
def create_radar_chart(self, parent=None) -> Figure:
"""创建雷达图 - 多维度指标对比"""
fig = plt.figure(figsize=(10, 10))
# 获取各维度评分
portfolio = self.results['portfolio_metrics']
scores = [
portfolio['dimension_scores']['risk_adjusted'],
portfolio['dimension_scores']['cashflow_quality'],
portfolio['dimension_scores']['financial_health'],
portfolio['dimension_scores']['operational_efficiency'],
portfolio['overall_score']
]
# 雷达图
categories = ['风险调整收益', '现金流质量', '财务健康度', '运营效率', '综合评分']
N = len(categories)
angles = [n / float(N) * 2 * 3.14159 for n in range(N)]
angles += angles[:1] # 闭合
scores += scores[:1] # 闭合
ax = fig.add_subplot(111, polar=True)
ax.plot(angles, scores, 'o-', linewidth=2, color='#1f77b4')
ax.fill(angles, scores, alpha=0.25, color='#1f77b4')
ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories, fontsize=11)
ax.set_ylim(0, 120)
ax.set_title('多维度评估雷达图', fontsize=14, fontweight='bold', pad=20)
# 添加评分标注
for angle, score in zip(angles[:-1], scores[:-1]):
ax.annotate(f'{score:.0f}', xy=(angle, score),
xytext=(angle, score + 10),
fontsize=10, ha='center')
plt.tight_layout()
return fig
def create_metrics_dashboard(self, parent=None) -> Figure:
"""创建指标仪表盘"""
fig = plt.figure(figsize=(16, 12))
gs = GridSpec(3, 3, figure=fig, hspace=0.3, wspace=0.3)
# 1. 投资评级
ax1 = fig.add_subplot(gs[0, 0])
rating = self.results['portfolio_metrics']['rating']
score = self.results['portfolio_metrics']['overall_score']
ax1.text(0.5, 0.5, rating, fontsize=60, ha='center', va='center',
fontweight='bold', color=self._get_color(score))
ax1.text(0.5, 0.15, f'综合评分: {score:.1f}', fontsize=12, ha='center', va='center')
ax1.set_xlim(0, 1)
ax1.set_ylim(0, 1)
ax1.axis('off')
ax1.set_title('投资评级', fontsize=14, fontweight='bold')
# 2. 回本周期
ax2 = fig.add_subplot(gs[0, 1])
be_days = self.results['break_even']['break_even_days']
be_quality = self.results['break_even']['payback_quality']
ax2.text(0.5, 0.6, f'{be_days:.0f}', fontsize=40, ha='center', va='center',
fontweight='bold', color=self._get_color(be_quality['score']))
ax2.text(0.5, 0.3, '天', fontsize=16, ha='center', va='center')
ax2.text(0.5, 0.1, f'({be_quality["quality"]})', fontsize=11, ha='center', va='center',
color=self._get_color(be_quality['score']))
ax2.set_xlim(0, 1)
ax2.set_ylim(0, 1)
ax2.axis('off')
ax2.set_title('回本周期', fontsize=14, fontweight='bold')
# 3. 年化ROI
ax3 = fig.add_subplot(gs[0, 2])
roi = self.results['break_even']['roi']
ax3.text(0.5, 0.6, f'{roi:.1f}%', fontsize=40, ha='center', va='center',
fontweight='bold', color=self._get_color(roi * 3))
ax3.text(0.5, 0.3, '年化ROI', fontsize=14, ha='center', va='center')
ax3.set_xlim(0, 1)
ax3.set_ylim(0, 1)
ax3.axis('off')
ax3.set_title('投资回报率', fontsize=14, fontweight='bold')
# 4. 夏普比率
ax4 = fig.add_subplot(gs[1, 0])
sharpe = self.results['risk_adjusted']['sharpe_ratio']
ax4.text(0.5, 0.6, f'{sharpe:.2f}', fontsize=36, ha='center', va='center',
fontweight='bold', color=self._get_color(sharpe * 40))
ax4.text(0.5, 0.2, '夏普比率', fontsize=12, ha='center', va='center')
ax4.set_xlim(0, 1)
ax4.set_ylim(0, 1)
ax4.axis('off')
ax4.set_title('风险调整收益', fontsize=14, fontweight='bold')
# 5. 现金流质量
ax5 = fig.add_subplot(gs[1, 1])
cf_ratio = self.results['cashflow_quality']['cf_to_income_ratio']
ax5.text(0.5, 0.6, f'{cf_ratio:.2f}', fontsize=36, ha='center', va='center',
fontweight='bold', color=self._get_color(cf_ratio * 50))
ax5.text(0.5, 0.2, '现金流/利润比', fontsize=11, ha='center', va='center')
ax5.set_xlim(0, 1)
ax5.set_ylim(0, 1)
ax5.axis('off')
ax5.set_title('现金流质量', fontsize=14, fontweight='bold')
# 6. 坪效
ax6 = fig.add_subplot(gs[1, 2])
sqm_eff = self.results['operational_efficiency']['efficiency_per_sqm']
ax6.text(0.5, 0.6, f'{sqm_eff:.0f}', fontsize=36, ha='center', va='center',
fontweight='bold', color=self._get_color(sqm_eff / 20))
ax6.text(0.5, 0.2, '元/㎡/月', fontsize=11, ha='center', va='center')
ax6.set_xlim(0, 1)
ax6.set_ylim(0, 1)
ax6.axis('off')
ax6.set_title('坪效', fontsize=14, fontweight='bold')
# 7. 关键指标条形图
ax7 = fig.add_subplot(gs[2, :])
metrics = {
'夏普比率': self.results['risk_adjusted']['sharpe_ratio'],
'索提诺比率': self.results['risk_adjusted']['sortino_ratio'],
'特雷诺比率': self.results['risk_adjusted']['treynor_ratio'],
'净利率': self.results['financial_health']['net_margin'],
'流动比率': self.results['financial_health']['current_ratio']
}
names = list(metrics.keys())
values = list(metrics.values())
colors = [self._get_color(v * 40 if '比率' in n else v) for n, v in metrics.items()]
bars = ax7.barh(names, values, color=colors)
ax7.set_xlabel('数值')
ax7.set_title('关键财务指标', fontsize=14, fontweight='bold')
ax7.axvline(x=1, color='gray', linestyle='--', alpha=0.5)
for bar, val in zip(bars, values):
ax7.text(val + 0.05, bar.get_y() + bar.get_height()/2,
f'{val:.2f}', va='center', fontsize=10)
plt.tight_layout()
return fig
def _get_color(self, score: float) -> str:
"""根据评分获取颜色"""
if score >= 80:
return '#2ecc71' # 绿色
elif score >= 60:
return '#3498db' # 蓝色
elif score >= 40:
return '#f39c12' # 橙色
else:
return '#e74c3c' # 红色
# ============================================================================
# PyQt5 主应用类
# ============================================================================
class InvestmentApp(QMainWindow):
"""投资计算评估应用主窗口"""
def __init__(self):
super().__init__()
self.calculator = InvestmentCalculator()
self.charts = None
self.init_ui()
def init_ui(self):
"""初始化UI"""
self.setWindowTitle('投资计算指标分析评估')
self.setGeometry(100, 100, 1400, 900)
# 创建菜单栏
self.create_menu_bar()
# 主布局
central_widget = QWidget()
self.setCentralWidget(central_widget)
main_layout = QHBoxLayout(central_widget)
# 左侧输入面板
left_panel = self.create_input_panel()
main_layout.addWidget(left_panel, 1)
# 右侧结果显示
right_panel = self.create_result_panel()
main_layout.addWidget(right_panel, 2)
# 状态栏
self.statusBar().showMessage('就绪')
def create_menu_bar(self):
"""创建菜单栏"""
menubar = self.menuBar()
# 文件菜单
file_menu = menubar.addMenu('文件')
new_action = QAction('新建项目', self)
new_action.triggered.connect(self.new_project)
file_menu.addAction(new_action)
save_action = QAction('保存报告', self)
save_action.triggered.connect(self.save_report)
file_menu.addAction(save_action)
export_action = QAction('导出图表', self)
export_action.triggered.connect(self.export_charts)
file_menu.addAction(export_action)
file_menu.addSeparator()
exit_action = QAction('退出', self)
exit_action.triggered.connect(self.close)
file_menu.addAction(exit_action)
# 视图菜单
view_menu = menubar.addMenu('视图')
self.show_basic_action = QAction('基础数据', self, checkable=True)
self.show_basic_action.setChecked(True)
view_menu.addAction(self.show_basic_action)
self.show_advanced_action = QAction('高级参数', self, checkable=True)
self.show_advanced_action.setChecked(True)
view_menu.addAction(self.show_advanced_action)
# 帮助菜单
help_menu = menubar.addMenu('帮助')
about_action = QAction('关于', self)
about_action.triggered.connect(self.show_about)
help_menu.addAction(about_action)
def create_input_panel(self) -> QWidget:
"""创建输入面板"""
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setMinimumWidth(400)
container = QWidget()
layout = QVBoxLayout(container)
layout.setSpacing(15)
# === 项目基本信息 ===
basic_group = QGroupBox('基本信息')
basic_layout = QGridLayout()
basic_layout.addWidget(QLabel('项目名称:'), 0, 0)
self.name_input = QLineEdit('店铺投资')
basic_layout.addWidget(self.name_input, 0, 1)
basic_layout.addWidget(QLabel('行业类型:'), 1, 0)
self.industry_combo = QComboBox()
self.industry_combo.addItems(list(IndustryBenchmarks.BENCHMARKS.keys()))
self.industry_combo.currentTextChanged.connect(self.on_industry_changed)
basic_layout.addWidget(self.industry_combo, 1, 1)
basic_layout.addWidget(QLabel('备注:'), 2, 0)
self.notes_input = QLineEdit()
basic_layout.addWidget(self.notes_input, 2, 1)
basic_group.setLayout(basic_layout)
layout.addWidget(basic_group)
# === 投资成本 ===
investment_group = QGroupBox('投资成本')
investment_layout = QGridLayout()
investment_layout.addWidget(QLabel('转让费(元):'), 0, 0)
self.transfer_fee = QSpinBox()
self.transfer_fee.setRange(0, 10000000)
self.transfer_fee.setValue(120000)
self.transfer_fee.setSuffix(' 元')
investment_layout.addWidget(self.transfer_fee, 0, 1)
investment_layout.addWidget(QLabel('月租金(元):'), 1, 0)
self.monthly_rent = QSpinBox()
self.monthly_rent.setRange(0, 1000000)
self.monthly_rent.setValue(2800)
self.monthly_rent.setSuffix(' 元')
investment_layout.addWidget(self.monthly_rent, 1, 1)
investment_layout.addWidget(QLabel('押金月数:'), 2, 0)
self.deposit_months = QSpinBox()
self.deposit_months.setRange(0, 12)
self.deposit_months.setValue(1)
investment_layout.addWidget(self.deposit_months, 2, 1)
investment_layout.addWidget(QLabel('预付月数:'), 3, 0)
self.advance_months = QSpinBox()
self.advance_months.setRange(0, 24)
self.advance_months.setValue(2)
investment_layout.addWidget(self.advance_months, 3, 1)
investment_layout.addWidget(QLabel('装修费(元):'), 4, 0)
self.renovation_cost = QSpinBox()
self.renovation_cost.setRange(0, 5000000)
self.renovation_cost.setValue(0)
self.renovation_cost.setSuffix(' 元')
investment_layout.addWidget(self.renovation_cost, 4, 1)
investment_layout.addWidget(QLabel('设备采购(元):'), 5, 0)
self.equipment_cost = QSpinBox()
self.equipment_cost.setRange(0, 5000000)
self.equipment_cost.setValue(0)
self.equipment_cost.setSuffix(' 元')
investment_layout.addWidget(self.equipment_cost, 5, 1)
investment_layout.addWidget(QLabel('其他费用(元):'), 6, 0)
self.other_costs = QSpinBox()
self.other_costs.setRange(0, 1000000)
self.other_costs.setValue(0)
self.other_costs.setSuffix(' 元')
investment_layout.addWidget(self.other_costs, 6, 1)
investment_group.setLayout(investment_layout)
layout.addWidget(investment_group)
# === 收入预测 ===
revenue_group = QGroupBox('收入预测')
revenue_layout = QGridLayout()
revenue_layout.addWidget(QLabel('日营业额(最低):'), 0, 0)
self.daily_revenue_min = QSpinBox()
self.daily_revenue_min.setRange(0, 100000)
self.daily_revenue_min.setValue(2500)
self.daily_revenue_min.setSuffix(' 元/天')
revenue_layout.addWidget(self.daily_revenue_min, 0, 1)
revenue_layout.addWidget(QLabel('日营业额(最高):'), 1, 0)
self.daily_revenue_max = QSpinBox()
self.daily_revenue_max.setRange(0, 100000)
self.daily_revenue_max.setValue(2800)
self.daily_revenue_max.setSuffix(' 元/天')
revenue_layout.addWidget(self.daily_revenue_max, 1, 1)
revenue_layout.addWidget(QLabel('月运营天数:'), 2, 0)
self.operating_days = QSpinBox()
self.operating_days.setRange(1, 31)
self.operating_days.setValue(30)
revenue_layout.addWidget(self.operating_days, 2, 1)
revenue_layout.addWidget(QLabel('预期利润率(%):'), 3, 0)
self.profit_rate = QDoubleSpinBox()
self.profit_rate.setRange(0, 100)
self.profit_rate.setValue(15)
self.profit_rate.setSuffix(' %')
revenue_layout.addWidget(self.profit_rate, 3, 1)
revenue_layout.addWidget(QLabel('月增长率(%):'), 4, 0)
self.growth_rate = QDoubleSpinBox()
self.growth_rate.setRange(-50, 100)
self.growth_rate.setValue(0)
self.growth_rate.setSuffix(' %')
revenue_layout.addWidget(self.growth_rate, 4, 1)
revenue_group.setLayout(revenue_layout)
layout.addWidget(revenue_group)
# === 成本结构(输入具体金额)===
cost_group = QGroupBox('成本结构(固定成本 - 按月实际金额输入)')
cost_layout = QGridLayout()
# 提示标签
hint_label = QLabel('💡 固定成本说明:租金、人力、水电、其他为每月固定支出,物料/进货成本由系统根据目标利润率自动校核')
hint_label.setStyleSheet('color: #666; font-size: 10px;')
hint_label.setWordWrap(True)
cost_layout.addWidget(hint_label, 0, 0, 1, 2)
cost_layout.addWidget(QLabel('月租金(元):'), 1, 0)
self.cost_rent = QSpinBox()
self.cost_rent.setRange(0, 1000000)
self.cost_rent.setValue(2800)
self.cost_rent.setSuffix(' 元/月')
cost_layout.addWidget(self.cost_rent, 1, 1)
cost_layout.addWidget(QLabel('月人力成本(元):'), 2, 0)
self.cost_labor = QSpinBox()
self.cost_labor.setRange(0, 1000000)
self.cost_labor.setValue(4000)
self.cost_labor.setSuffix(' 元/月')
cost_layout.addWidget(self.cost_labor, 2, 1)
cost_layout.addWidget(QLabel('月食材/物料(元):'), 3, 0)
self.cost_material = QSpinBox()
self.cost_material.setRange(0, 1000000)
self.cost_material.setValue(15000)
self.cost_material.setSuffix(' 元/月')
cost_layout.addWidget(self.cost_material, 3, 1)
cost_layout.addWidget(QLabel('月水电费(元):'), 4, 0)
self.cost_utilities = QSpinBox()
self.cost_utilities.setRange(0, 100000)
self.cost_utilities.setValue(700)
self.cost_utilities.setSuffix(' 元/月')
cost_layout.addWidget(self.cost_utilities, 4, 1)
cost_layout.addWidget(QLabel('月其他费用(元):'), 5, 0)
self.cost_other = QSpinBox()
self.cost_other.setRange(0, 100000)
self.cost_other.setValue(1000)
self.cost_other.setSuffix(' 元/月')
cost_layout.addWidget(self.cost_other, 5, 1)
# 显示计算后的占比
self.ratio_display = QLabel('成本占比将在计算后显示')
self.ratio_display.setStyleSheet('color: #333; font-size: 11px; padding: 5px; background: #f5f5f5; border-radius: 3px;')
self.ratio_display.setWordWrap(True)
cost_layout.addWidget(self.ratio_display, 6, 0, 1, 2)
# 允许成本提示
self.allowed_cost_label = QLabel('允许成本将在计算后显示')
self.allowed_cost_label.setStyleSheet('color: #e74c3c; font-size: 10px; padding: 3px; background: #fff3f3; border-radius: 3px;')
self.allowed_cost_label.setWordWrap(True)
cost_layout.addWidget(self.allowed_cost_label, 7, 0, 1, 2)
cost_group.setLayout(cost_layout)
layout.addWidget(cost_group)
# === 运营参数 ===
operation_group = QGroupBox('运营参数')
operation_layout = QGridLayout()
operation_layout.addWidget(QLabel('经营面积(㎡):'), 0, 0)
self.area = QSpinBox()
self.area.setRange(1, 10000)
self.area.setValue(100)
operation_layout.addWidget(self.area, 0, 1)
operation_layout.addWidget(QLabel('员工数量:'), 1, 0)
self.staff_count = QSpinBox()
self.staff_count.setRange(1, 100)
self.staff_count.setValue(2)
operation_layout.addWidget(self.staff_count, 1, 1)
operation_layout.addWidget(QLabel('座位数:'), 2, 0)
self.seats = QSpinBox()
self.seats.setRange(0, 1000)
self.seats.setValue(20)
operation_layout.addWidget(self.seats, 2, 1)
operation_layout.addWidget(QLabel('日客流量:'), 3, 0)
self.daily_customers = QSpinBox()
self.daily_customers.setRange(0, 10000)
self.daily_customers.setValue(50)
operation_layout.addWidget(self.daily_customers, 3, 1)
operation_group.setLayout(operation_layout)
layout.addWidget(operation_group)
# === 风险参数 ===
risk_group = QGroupBox('风险参数(高级)')
risk_layout = QGridLayout()
risk_layout.addWidget(QLabel('无风险利率(%):'), 0, 0)
self.risk_free_rate = QDoubleSpinBox()
self.risk_free_rate.setRange(0, 20)
self.risk_free_rate.setValue(2.5)
self.risk_free_rate.setSuffix(' %')
risk_layout.addWidget(self.risk_free_rate, 0, 1)
risk_layout.addWidget(QLabel('收入波动率(%):'), 1, 0)
self.revenue_volatility = QDoubleSpinBox()
self.revenue_volatility.setRange(0, 100)
self.revenue_volatility.setValue(10)
self.revenue_volatility.setSuffix(' %')
risk_layout.addWidget(self.revenue_volatility, 1, 1)
risk_layout.addWidget(QLabel('市场贝塔:'), 2, 0)
self.market_beta = QDoubleSpinBox()
self.market_beta.setRange(0.1, 3)
self.market_beta.setValue(1.0)
self.market_beta.setSingleStep(0.1)
risk_layout.addWidget(self.market_beta, 2, 1)
risk_layout.addWidget(QLabel('行业贝塔:'), 3, 0)
self.industry_beta = QDoubleSpinBox()
self.industry_beta.setRange(0.1, 3)
self.industry_beta.setValue(1.2)
self.industry_beta.setSingleStep(0.1)
risk_layout.addWidget(self.industry_beta, 3, 1)
risk_group.setLayout(risk_layout)
layout.addWidget(risk_group)
# === 计算按钮 ===
calc_button = QPushButton('开始计算评估')
calc_button.setMinimumHeight(50)
calc_button.setStyleSheet('''
QPushButton {
background-color: #3498db;
color: white;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
}
QPushButton:hover {
background-color: #2980b9;
}
''')
calc_button.clicked.connect(self.calculate)
layout.addWidget(calc_button)
layout.addStretch()
scroll.setWidget(container)
return scroll
def create_result_panel(self) -> QWidget:
"""创建结果面板"""
container = QWidget()
layout = QVBoxLayout(container)
# 标签页
self.tabs = QTabWidget()
# 概览标签
self.overview_tab = self.create_overview_tab()
self.tabs.addTab(self.overview_tab, '评估概览')
# 回本分析标签
self.breakeven_tab = self.create_breakeven_tab()
self.tabs.addTab(self.breakeven_tab, '回本周期分析')
# 指标分析标签
self.metrics_tab = self.create_metrics_tab()
self.tabs.addTab(self.metrics_tab, '专业指标')
# 图表标签
self.chart_tab = self.create_chart_tab()
self.tabs.addTab(self.chart_tab, '可视化图表')
# 详细报告标签
self.report_tab = self.create_report_tab()
self.tabs.addTab(self.report_tab, '详细报告')
layout.addWidget(self.tabs)
return container
def create_overview_tab(self) -> QWidget:
"""创建概览标签页"""
widget = QWidget()
layout = QVBoxLayout(widget)
# 投资评级卡片
self.rating_card = self.create_metric_card('投资评级', 'A', '#2ecc71')
layout.addWidget(self.rating_card)
# 关键指标网格
grid = QGridLayout()
self.breakeven_card = self.create_metric_card('回本周期', '--- 天', '#3498db')
grid.addWidget(self.breakeven_card, 0, 0)
self.roi_card = self.create_metric_card('年化ROI', '--- %', '#9b59b6')
grid.addWidget(self.roi_card, 0, 1)
self.sharpe_card = self.create_metric_card('夏普比率', '---', '#e67e22')
grid.addWidget(self.sharpe_card, 1, 0)
self.profit_card = self.create_metric_card('月利润', '--- 元', '#1abc9c')
grid.addWidget(self.profit_card, 1, 1)
layout.addLayout(grid)
# 初步评估结果
self.overview_text = QTextEdit()
self.overview_text.setReadOnly(True)
self.overview_text.setPlaceholderText('点击"开始计算评估"查看结果...')
layout.addWidget(self.overview_text)
return widget
def create_metric_card(self, title: str, value: str, color: str) -> QWidget:
"""创建指标卡片"""
card = QFrame()
card.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
card.setStyleSheet(f'''
QFrame {{
border: 2px solid {color};
border-radius: 10px;
background-color: white;
}}
''')
layout = QVBoxLayout(card)
layout.setContentsMargins(15, 10, 15, 10)
title_label = QLabel(title)
title_label.setStyleSheet('color: #666; font-size: 12px;')
title_label.setAlignment(Qt.AlignCenter)
layout.addWidget(title_label)
value_label = QLabel(value)
value_label.setObjectName('valueLabel')
value_label.setStyleSheet(f'color: {color}; font-size: 28px; font-weight: bold;')
value_label.setAlignment(Qt.AlignCenter)
layout.addWidget(value_label)
return card
def create_breakeven_tab(self) -> QWidget:
"""创建回本周期标签页"""
widget = QWidget()
layout = QVBoxLayout(widget)
# 回本周期图表
self.breakeven_canvas = FigureCanvas(Figure(figsize=(10, 6)))
layout.addWidget(self.breakeven_canvas)
# 详细数据表
self.breakeven_table = QTableWidget()
self.breakeven_table.setColumnCount(4)
self.breakeven_table.setHorizontalHeaderLabels(['指标', '实际值', '行业基准', '评估'])
self.breakeven_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
layout.addWidget(self.breakeven_table)
return widget
def create_metrics_tab(self) -> QWidget:
"""创建专业指标标签页"""
widget = QWidget()
layout = QVBoxLayout(widget)
# 指标表格
self.metrics_table = QTableWidget()
self.metrics_table.setColumnCount(4)
self.metrics_table.setHorizontalHeaderLabels(['指标类别', '指标名称', '数值', '评估'])
self.metrics_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
layout.addWidget(self.metrics_table)
return widget
def create_chart_tab(self) -> QWidget:
"""创建图表标签页"""
widget = QWidget()
layout = QVBoxLayout(widget)
# 图表类型选择
chart_selector = QHBoxLayout()
chart_selector.addWidget(QLabel('图表类型:'))
self.chart_type_combo = QComboBox()
self.chart_type_combo.addItems([
'投资仪表盘',
'成本结构分析',
'月度预测',
'多维度雷达图'
])
chart_selector.addWidget(self.chart_type_combo)
chart_selector.addStretch()
self.chart_type_combo.currentIndexChanged.connect(self.update_chart)
layout.addLayout(chart_selector)
# 图表画布
self.chart_canvas = FigureCanvas(Figure(figsize=(12, 8)))
layout.addWidget(self.chart_canvas)
return widget
def create_report_tab(self) -> QWidget:
"""创建详细报告标签页"""
widget = QWidget()
layout = QVBoxLayout(widget)
# 报告文本
self.report_text = QTextEdit()
self.report_text.setReadOnly(True)
layout.addWidget(self.report_text)
return widget
def on_industry_changed(self, industry: str):
"""行业变化时更新成本默认值"""
benchmark = IndustryBenchmarks.get_benchmark(industry)
# 获取当前月收入估算
daily_avg = (self.daily_revenue_min.value() + self.daily_revenue_max.value()) / 2
monthly_revenue = daily_avg * self.operating_days.value()
# 使用租金输入框的值
monthly_rent_actual = self.monthly_rent.value()
# 根据行业基准比例估算成本(保持合理的总成本占比)
rent_ratio_avg = sum(benchmark.get('rent_ratio', (12, 18))) / 200
labor_ratio_avg = sum(benchmark.get('labor_cost_ratio', (18, 22))) / 200
material_ratio_avg = sum(benchmark.get('material_ratio', (30, 35))) / 200
utilities_ratio_avg = 0.03 # 水电约3%
other_ratio_avg = 0.05 # 其他约5%
# 总成本占比(1 - 目标利润率)
target_profit_rate = 0.15 # 15%目标利润率
total_cost_ratio = 1 - target_profit_rate # 85%
# 更新成本输入框
self.cost_rent.setValue(monthly_rent_actual)
self.cost_labor.setValue(max(2000, int(monthly_revenue * labor_ratio_avg * total_cost_ratio)))
self.cost_material.setValue(max(3000, int(monthly_revenue * material_ratio_avg * total_cost_ratio)))
self.cost_utilities.setValue(max(300, int(monthly_revenue * utilities_ratio_avg * total_cost_ratio)))
self.cost_other.setValue(max(500, int(monthly_revenue * other_ratio_avg * total_cost_ratio)))
def collect_input_data(self) -> Dict:
"""收集输入数据"""
return {
'name': self.name_input.text(),
'industry': self.industry_combo.currentText(),
'notes': self.notes_input.text(),
'transfer_fee': self.transfer_fee.value(),
'monthly_rent': self.monthly_rent.value(),
'deposit_months': self.deposit_months.value(),
'advance_months': self.advance_months.value(),
'renovation_cost': self.renovation_cost.value(),
'equipment_cost': self.equipment_cost.value(),
'other_costs': self.other_costs.value(),
'daily_revenue_min': self.daily_revenue_min.value(),
'daily_revenue_max': self.daily_revenue_max.value(),
'operating_days': self.operating_days.value(),
'profit_rate': self.profit_rate.value() / 100,
'growth_rate': self.growth_rate.value() / 100,
# 成本结构 - 实际金额
'cost_rent': self.cost_rent.value(),
'cost_labor': self.cost_labor.value(),
'cost_material': self.cost_material.value(),
'cost_utilities': self.cost_utilities.value(),
'cost_other': self.cost_other.value(),
# 以下保留百分比计算
'rent_ratio': self.cost_rent.value() / 100,
'labor_ratio': self.cost_labor.value() / 100,
'material_ratio': self.cost_material.value() / 100,
'utilities_ratio': self.cost_utilities.value() / 100,
'other_ratio': self.cost_other.value() / 100,
'area': self.area.value(),
'staff_count': self.staff_count.value(),
'seats': self.seats.value(),
'daily_customers': self.daily_customers.value(),
'risk_free_rate': self.risk_free_rate.value() / 100,
'daily_revenue_volatility': self.revenue_volatility.value() / 100,
'market_beta': self.market_beta.value(),
'industry_beta': self.industry_beta.value(),
}
def calculate(self):
"""执行计算"""
self.statusBar().showMessage('正在计算...')
QApplication.processEvents() # 刷新界面
try:
# 收集数据
data = self.collect_input_data()
self.calculator.set_data(data)
# 执行计算
results = self.calculator.generate_full_report()
self.results = results
# 检查数据合理性
self.check_data_reasonable(results)
# 创建图表对象
self.charts = InvestmentCharts(data, results)
# 更新UI
self.update_overview(results)
self.update_breakeven(results)
self.update_metrics(results)
self.update_chart(0)
self.update_report(results)
# 更新成本占比显示
costs = results['costs']
target_rate = data['profit_rate'] * 100
actual_profit_rate = costs['profit_rate'] * 100
profit_diff = costs['profit_diff']
self.ratio_display.setText(
f"成本占比: 租金{costs['rent_ratio']*100:.1f}% | "
f"人力{costs['labor_ratio']*100:.1f}% | "
f"进货{costs['material_ratio']*100:.1f}% | "
f"水电{costs['utilities_ratio']*100:.1f}% | "
f"其他{costs['other_ratio']*100:.1f}%"
)
# 真实反映实际利润率与目标的关系
if profit_diff >= 0:
self.allowed_cost_label.setText(
f"✓ 实际利润率: {actual_profit_rate:.1f}% (超出目标 {target_rate:.0f}% {profit_diff:,.0f}元)"
)
self.allowed_cost_label.setStyleSheet('color: #27ae60; font-size: 10px; padding: 3px; background: #eafaf1; border-radius: 3px;')
else:
self.allowed_cost_label.setText(
f"实际利润率: {actual_profit_rate:.1f}% (低于目标 {target_rate:.0f}% {profit_diff:,.0f}元)"
)
self.allowed_cost_label.setStyleSheet('color: #e74c3c; font-size: 10px; padding: 3px; background: #fff3f3; border-radius: 3px;')
self.statusBar().showMessage('计算完成', 3000)
except Exception as e:
import traceback
QMessageBox.critical(self, '错误', f'计算过程出错:\n{str(e)}\n\n{traceback.format_exc()}')
self.statusBar().showMessage('计算失败', 3000)
def check_data_reasonable(self, results: Dict):
"""数据合理性提醒 - 仅供参考"""
costs = results['costs']
revenue = results['revenue']
break_even = results['break_even']
# 仅作为信息提示,不阻碍计算
info_messages = []
# 盈亏提示
if costs['monthly_profit'] < 0:
info_messages.append(f"📉 当前配置为亏损状态:月亏损 {-costs['monthly_profit']:,.0f} 元")
else:
info_messages.append(f"📈 当前配置为盈利状态:月盈利 {costs['monthly_profit']:,.0f} 元")
# 回本周期提醒
if break_even['break_even_days'] > 365 * 5:
info_messages.append(f"⚠️ 回本周期超过5年,需长期经营才有回报")
elif break_even['break_even_days'] == float('inf'):
info_messages.append(f"⚠️ 项目处于持续亏损状态,无法回本")
# 成本结构提示(与行业对比)
industry = self.calculator.data.get('industry', '餐饮')
benchmark = IndustryBenchmarks.get_benchmark(industry)
material_ratio = costs['material_ratio'] * 100
material_benchmark = benchmark.get('material_ratio', (30, 35))
if material_ratio > material_benchmark[1]:
info_messages.append(f"📊 物料/进货成本占比({material_ratio:.1f}%)高于行业参考上限({material_benchmark[1]}%)")
elif material_ratio < material_benchmark[0] * 0.5 and material_ratio > 0:
info_messages.append(f"📊 物料/进货成本占比({material_ratio:.1f}%)低于行业参考下限({material_benchmark[0]}%)")
if info_messages:
msg = "以下是数据参考信息(非错误):\n\n" + "\n".join(info_messages)
QMessageBox.information(self, '数据参考信息', msg)
def update_overview(self, results: Dict):
"""更新概览"""
portfolio = results['portfolio_metrics']
break_even = results['break_even']
# 更新评级卡片
rating_label = self.rating_card.findChild(QLabel, 'valueLabel')
rating_label.setText(portfolio['rating'])
rating_label.setStyleSheet(f'color: {self._get_color(portfolio["overall_score"])}; font-size: 28px; font-weight: bold;')
# 更新指标卡片
self._update_card(self.breakeven_card, f'{break_even["break_even_days"]:.0f} 天')
self._update_card(self.roi_card, f'{break_even["roi"]:.1f} %')
self._update_card(self.sharpe_card, f'{results["risk_adjusted"]["sharpe_ratio"]:.2f}')
self._update_card(self.profit_card, f'{break_even["monthly_profit"]:,.0f} 元')
# 更新概览文本
costs = results['costs']
revenue = results['revenue']
target_rate = self.calculator.data.get('profit_rate', 0.15) * 100
actual_rate = costs['profit_rate'] * 100
profit_status = "盈利" if costs['monthly_profit'] >= 0 else "亏损"
profit_color = "#27ae60" if costs['monthly_profit'] >= 0 else "#e74c3c"
overview = f"""
<h2>投资评估概览</h2>
<p><b>项目名称:</b> {self.calculator.data.get('name', '店铺投资')}</p>
<p><b>行业类型:</b> {IndustryBenchmarks.get_benchmark(self.calculator.data.get('industry', '自定义'))['name']}</p>
<h3>核心结论</h3>
<p><b>投资评级:</b> <span style="color: {self._get_color(portfolio['overall_score'])}">{portfolio['rating']}</span> - {portfolio['recommendation']}</p>
<p><b>综合评分:</b> {portfolio['overall_score']:.1f}/100</p>
<h3>📊 盈亏计算(如实计算)</h3>
<table style="width:100%; border-collapse: collapse;">
<tr style="background: #e8f5e9;"><td><b>月营业额(平均)</b></td><td>{revenue['monthly_revenue_avg']:,.0f} 元</td><td>({revenue['daily_revenue_avg']:.0f}元/天 × 30天)</td></tr>
<tr style="background: #f8f9fa;"><td>月租金</td><td>{costs['monthly_rent']:,.0f} 元</td><td>{costs['rent_ratio']*100:.1f}%</td></tr>
<tr style="background: #f8f9fa;"><td>月人力成本</td><td>{costs['monthly_labor']:,.0f} 元</td><td>{costs['labor_ratio']*100:.1f}%</td></tr>
<tr style="background: #f8f9fa;"><td>月进货/物料成本</td><td>{costs['monthly_material']:,.0f} 元</td><td>{costs['material_ratio']*100:.1f}%</td></tr>
<tr style="background: #f8f9fa;"><td>月水电费</td><td>{costs['monthly_utilities']:,.0f} 元</td><td>{costs['utilities_ratio']*100:.1f}%</td></tr>
<tr style="background: #f8f9fa;"><td>月其他费用</td><td>{costs['monthly_other']:,.0f} 元</td><td>{costs['other_ratio']*100:.1f}%</td></tr>
<tr style="background: #fce4ec;"><td><b>月总成本</b></td><td><b>{costs['monthly_total_cost']:,.0f} 元</b></td><td>{costs['rent_ratio']*100+costs['labor_ratio']*100+costs['material_ratio']*100+costs['utilities_ratio']*100+costs['other_ratio']*100:.1f}%</td></tr>
<tr style="background: #e3f2fd;"><td><b>月净利润</b></td><td><b style="color: {profit_color}">{break_even['monthly_profit']:,.0f} 元</b></td><td><b style="color: {profit_color}">{actual_rate:.1f}%</b> ({profit_status})</td></tr>
</table>
<h3>💡 与目标利润率对比</h3>
<table style="width:100%; border-collapse: collapse;">
<tr><td><b>目标利润率</b></td><td>{target_rate:.0f}%</td><td>目标月利润: {costs['target_profit']:,.0f} 元</td></tr>
<tr><td><b>实际利润率</b></td><td>{actual_rate:.1f}%</td><td>实际月利润: {break_even['monthly_profit']:,.0f} 元</td></tr>
<tr><td><b>差额</b></td><td colspan="2" style="color: {profit_color}">
{'+' if costs['profit_diff'] >= 0 else ''}{costs['profit_diff']:,.0f} 元
({'超出目标' if costs['profit_diff'] >= 0 else '低于目标'})
</td></tr>
</table>
<h3>关键指标摘要</h3>
<table style="width:100%">
<tr><td><b>回本周期:</b></td><td>{break_even['break_even_days']:.0f}天 ({break_even['break_even_months']:.1f}个月)</td></tr>
<tr><td><b>年化ROI:</b></td><td>{break_even['roi']:.1f}%</td></tr>
<tr><td><b>总投入:</b></td><td>{break_even['total_investment']:,.0f}元</td></tr>
</table>
<h3>风险评估</h3>
<p><b>夏普比率:</b> {results['risk_adjusted']['sharpe_ratio']:.2f} ({results['risk_adjusted']['sharpe_assessment']['level']})</p>
<p><b>索提诺比率:</b> {results['risk_adjusted']['sortino_ratio']:.2f} ({results['risk_adjusted']['sortino_assessment']['level']})</p>
<p><b>特雷诺比率:</b> {results['risk_adjusted']['treynor_ratio']:.2f} ({results['risk_adjusted']['treynor_assessment']['level']})</p>
<h3>现金流评估</h3>
<p><b>现金流质量:</b> {results['cashflow_quality']['quality_assessment']['level']}</p>
<p><b>现金流/利润比:</b> {results['cashflow_quality']['cf_to_income_ratio']:.2f}</p>
<h3>专业建议</h3>
<p>{break_even['payback_quality']['suggestion']}</p>
<p>{results['cashflow_quality']['quality_assessment']['suggestion']}</p>
<p>{portfolio['correlation_suggestion']['suggestion']}</p>
"""
self.overview_text.setHtml(overview)
def _update_card(self, card: QFrame, value: str):
"""更新指标卡片"""
value_label = card.findChild(QLabel, 'valueLabel')
if value_label:
value_label.setText(value)
def _get_color(self, score: float) -> str:
"""根据评分获取颜色"""
if score >= 80:
return '#2ecc71'
elif score >= 60:
return '#3498db'
elif score >= 40:
return '#f39c12'
else:
return '#e74c3c'
def update_breakeven(self, results: Dict):
"""更新回本周期标签页"""
# 更新图表
self.breakeven_canvas.figure.clear()
fig = self.charts.create_break_even_chart()
self.breakeven_canvas.figure = fig
self.breakeven_canvas.draw()
# 更新表格
break_even = results['break_even']
investment = results['initial_investment']
industry = self.calculator.data.get('industry', '自定义')
benchmark = IndustryBenchmarks.get_benchmark(industry)
self.breakeven_table.setRowCount(6)
data = [
('总初始投入', f'{investment["total_investment"]:,.0f} 元', '-', '需回收'),
('月利润', f'{break_even["monthly_profit"]:,.0f} 元', '-', '持续盈利'),
('回本周期', f'{break_even["break_even_days"]:.0f} 天', f'{benchmark["break_even_optimal"][0]}-{benchmark["break_even_optimal"][1]}个月', break_even['payback_quality']['quality']),
('年化ROI', f'{break_even["roi"]:.1f} %', '25%-50%', '优质' if break_even['roi'] >= 25 else '一般'),
('3年总回报', f'{break_even["annual_profit"]*3 - investment["total_investment"]:,.0f} 元', '-', '长期价值'),
('投资回收期', f'{break_even["break_even_months"]:.1f} 个月', f'{benchmark["break_even_risky"]}个月风险线', '安全' if break_even['break_even_months'] <= benchmark['break_even_risky'] else '风险'),
]
for i, (name, value, benchmark_val, assessment) in enumerate(data):
self.breakeven_table.setItem(i, 0, QTableWidgetItem(name))
self.breakeven_table.setItem(i, 1, QTableWidgetItem(value))
self.breakeven_table.setItem(i, 2, QTableWidgetItem(benchmark_val))
self.breakeven_table.setItem(i, 3, QTableWidgetItem(assessment))
def update_metrics(self, results: Dict):
"""更新专业指标标签页"""
risk = results['risk_adjusted']
cashflow = results['cashflow_quality']
financial = results['financial_health']
operation = results['operational_efficiency']
# 设置行数
self.metrics_table.setRowCount(18)
data = [
# 风险调整收益指标
('风险调整收益', '夏普比率', f'{risk["sharpe_ratio"]:.3f}', risk['sharpe_assessment']['level']),
('', '索提诺比率', f'{risk["sortino_ratio"]:.3f}', risk['sortino_assessment']['level']),
('', '特雷诺比率', f'{risk["treynor_ratio"]:.3f}', risk['treynor_assessment']['level']),
('', '年化波动率', f'{risk["annual_volatility"]*100:.1f}%', '-' if risk['annual_volatility'] < 0.15 else '注意'),
('', '贝塔系数', f'{risk["beta"]:.2f}', '-' if risk['beta'] <= 1.2 else '偏高'),
# 现金流质量指标
('现金流质量', '经营现金流/利润', f'{cashflow["cf_to_income_ratio"]:.2f}', cashflow['quality_assessment']['level']),
('', '自由现金流', f'{cashflow["free_cashflow"]:,.0f}元', '正数' if cashflow['free_cashflow'] > 0 else '负数'),
('', '现金流稳定性', f'{cashflow["stability_assessment"]["volatility"]*100:.0f}%', cashflow['stability_assessment']['level']),
# 财务健康度指标
('财务健康度', '毛利率', f'{financial["gross_margin"]:.1f}%', financial['gross_margin_assessment']['level']),
('', '净利率', f'{financial["net_margin"]:.1f}%', financial['net_margin_assessment']['level']),
('', '资产负债率', f'{financial["debt_ratio"]:.1f}%', financial['debt_ratio_assessment']['level']),
('', '流动比率', f'{financial["current_ratio"]:.2f}', financial['current_ratio_assessment']['level']),
# 运营效率指标
('运营效率', '坪效', f'{operation["efficiency_per_sqm"]:,.0f}元/㎡', operation['sqm_efficiency_assessment']['level']),
('', '人效', f'{operation["efficiency_per_person"]:,.0f}元/人', operation['person_efficiency_assessment']['level']),
('', '存货周转天数', f'{operation["inventory_days"]:.0f}天', operation['inventory_assessment']['level']),
('', '翻台率', f'{operation["turnover_rate"]:.1f}次', operation['turnover_assessment']['level']),
('', '客单价', f'{operation["avg_customer_value"]:.0f}元', '-'),
]
for i, (category, name, value, assessment) in enumerate(data):
self.metrics_table.setItem(i, 0, QTableWidgetItem(category))
self.metrics_table.setItem(i, 1, QTableWidgetItem(name))
self.metrics_table.setItem(i, 2, QTableWidgetItem(value))
self.metrics_table.setItem(i, 3, QTableWidgetItem(assessment))
def update_chart(self, index: int):
"""更新图表"""
if not self.charts:
return
self.chart_canvas.figure.clear()
chart_types = {
0: self.charts.create_metrics_dashboard,
1: self.charts.create_cost_structure_chart,
2: self.charts.create_monthly_projection_chart,
3: self.charts.create_radar_chart,
}
fig = chart_types.get(index, self.charts.create_metrics_dashboard)()
self.chart_canvas.figure = fig
self.chart_canvas.draw()
def update_report(self, results: Dict):
"""更新详细报告"""
portfolio = results['portfolio_metrics']
break_even = results['break_even']
risk = results['risk_adjusted']
cashflow = results['cashflow_quality']
financial = results['financial_health']
operation = results['operational_efficiency']
investment = results['initial_investment']
report = f"""
<h1>投资评估详细报告</h1>
<h2>一、项目基本信息</h2>
<p><b>项目名称:</b> {self.calculator.data.get('name', '店铺投资')}</p>
<p><b>行业类型:</b> {IndustryBenchmarks.get_benchmark(self.calculator.data.get('industry', '自定义'))['name']}</p>
<p><b>评估时间:</b> {datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
<h2>二、投资成本明细</h2>
<table style="width:100%">
<tr><td>转让费</td><td>{investment['investment_breakdown']['转让费']:,.0f} 元</td></tr>
<tr><td>首期租金</td><td>{investment['investment_breakdown']['首期租金']:,.0f} 元</td></tr>
<tr><td>装修费</td><td>{investment['investment_breakdown']['装修费']:,.0f} 元</td></tr>
<tr><td>设备费</td><td>{investment['investment_breakdown']['设备费']:,.0f} 元</td></tr>
<tr><td>其他费用</td><td>{investment['investment_breakdown']['其他费用']:,.0f} 元</td></tr>
<tr><td><b>总投资</b></td><td><b>{investment['total_investment']:,.0f} 元</b></td></tr>
</table>
<h2>三、回本周期分析</h2>
<p><b>回本周期:</b> {break_even['break_even_days']:.0f} 天 ({break_even['break_even_months']:.1f} 个月)</p>
<p><b>评估结果:</b> <span style="color:{self._get_color(break_even['payback_quality']['score'])}">{break_even['payback_quality']['quality']}</span></p>
<p><b>专业建议:</b> {break_even['payback_quality']['suggestion']}</p>
<p><b>年化ROI:</b> {break_even['roi']:.1f}%</p>
<p><b>3年总回报:</b> {break_even['annual_profit']*3 - investment['total_investment']:,.0f} 元</p>
<h2>四、风险调整后收益指标</h2>
<table style="width:100%">
<tr><td><b>夏普比率</b></td><td>{risk['sharpe_ratio']:.3f}</td><td>{risk['sharpe_assessment']['suggestion']}</td></tr>
<tr><td><b>索提诺比率</b></td><td>{risk['sortino_ratio']:.3f}</td><td>{risk['sortino_assessment']['suggestion']}</td></tr>
<tr><td><b>特雷诺比率</b></td><td>{risk['treynor_ratio']:.3f}</td><td>{risk['treynor_assessment']['suggestion']}</td></tr>
<tr><td><b>年化波动率</b></td><td>{risk['annual_volatility']*100:.1f}%</td><td>-</td></tr>
<tr><td><b>贝塔系数</b></td><td>{risk['beta']:.2f}</td><td>-</td></tr>
</table>
<h2>五、现金流质量评估</h2>
<p><b>现金流/利润比:</b> {cashflow['cf_to_income_ratio']:.2f}</p>
<p><b>自由现金流:</b> {cashflow['free_cashflow']:,.0f} 元/月</p>
<p><b>质量评估:</b> <span style="color:{self._get_color(cashflow['quality_assessment']['score'])}">{cashflow['quality_assessment']['level']}</span></p>
<p><b>专业建议:</b> {cashflow['quality_assessment']['suggestion']}</p>
<h2>六、财务健康度</h2>
<table style="width:100%">
<tr><td><b>毛利率</b></td><td>{financial['gross_margin']:.1f}%</td><td>{financial['gross_margin_assessment']['level']}</td></tr>
<tr><td><b>净利率</b></td><td>{financial['net_margin']:.1f}%</td><td>{financial['net_margin_assessment']['level']}</td></tr>
<tr><td><b>资产负债率</b></td><td>{financial['debt_ratio']:.1f}%</td><td>{financial['debt_ratio_assessment']['level']}</td></tr>
<tr><td><b>流动比率</b></td><td>{financial['current_ratio']:.2f}</td><td>{financial['current_ratio_assessment']['level']}</td></tr>
</table>
<p><b>成本结构评估:</b> {financial['cost_structure_assessment']['suggestion']}</p>
<h2>七、运营效率指标</h2>
<table style="width:100%">
<tr><td><b>坪效</b></td><td>{operation['efficiency_per_sqm']:,.0f} 元/㎡/月</td><td>{operation['sqm_efficiency_assessment']['level']}</td></tr>
<tr><td><b>人效</b></td><td>{operation['efficiency_per_person']:,.0f} 元/人/月</td><td>{operation['person_efficiency_assessment']['level']}</td></tr>
<tr><td><b>存货周转天数</b></td><td>{operation['inventory_days']:.0f} 天</td><td>{operation['inventory_assessment']['level']}</td></tr>
</table>
<h2>八、综合投资评级</h2>
<p><b>投资评级:</b> <span style="font-size:24px;color:{self._get_color(portfolio['overall_score'])}">{portfolio['rating']}</span></p>
<p><b>综合评分:</b> {portfolio['overall_score']:.1f}/100</p>
<p><b>投资建议:</b> <span style="color:{self._get_color(portfolio['overall_score'])}">{portfolio['recommendation']}</span></p>
<h2>九、投资组合建议</h2>
<p>{portfolio['correlation_suggestion']['suggestion']}</p>
<p><b>建议组合规模:</b> {portfolio['correlation_suggestion']['ideal_portfolio_size']} 个不同项目</p>
<p><b>单项目最大占比:</b> {portfolio['correlation_suggestion']['max_concentration']}%</p>
<h2>十、专业投资者决策要点</h2>
<ol>
<li><b>回本周期验证:</b> 本项目{break_even['break_even_months']:.1f}个月{'符合' if break_even['break_even_months'] <= 18 else '超过'}行业优质标准(6-18个月)</li>
<li><b>风险收益比:</b> 夏普比率{risk['sharpe_ratio']:.2f},{'优秀' if risk['sharpe_ratio'] >= 1.2 else '良好' if risk['sharpe_ratio'] >= 0.8 else '一般'}</li>
<li><b>现金流质量:</b> 现金流转化率{cashflow['cf_to_income_ratio']:.0%},{'表明盈利可持续' if cashflow['cf_to_income_ratio'] >= 1 else '需关注回款质量'}</li>
<li><b>成本结构:</b> {'成本结构合理' if not financial['cost_structure_assessment']['issues'] else '存在成本异常: ' + '; '.join(financial['cost_structure_assessment']['issues'])}</li>
<li><b>3年总回报率:</b> {(break_even['annual_profit']*3 / investment['total_investment'])*100:.0f}%,{'优秀' if break_even['annual_profit']*3 >= investment['total_investment']*1.5 else '达到' if break_even['annual_profit']*3 >= investment['total_investment'] else '未达标'}专业投资者标准</li>
</ol>
<hr>
<p style="color:#666;text-align:center">本报告由投资计算评估系统自动生成,仅供参考</p>
"""
self.report_text.setHtml(report)
def new_project(self):
"""新建项目"""
reply = QMessageBox.question(self, '确认', '确定要新建项目吗?当前未保存的数据将丢失。',
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
# 重置所有输入
self.transfer_fee.setValue(120000)
self.monthly_rent.setValue(2800)
self.deposit_months.setValue(1)
self.advance_months.setValue(2)
self.daily_revenue_min.setValue(2500)
self.daily_revenue_max.setValue(2800)
self.profit_rate.setValue(15)
self.statusBar().showMessage('已新建项目')
def save_report(self):
"""保存报告"""
if not hasattr(self, 'results'):
QMessageBox.warning(self, '提示', '请先进行计算')
return
filename, _ = QFileDialog.getSaveFileName(self, '保存报告',
f'投资评估报告_{datetime.now().strftime("%Y%m%d")}.html',
'HTML文件 (*.html)')
if filename:
try:
with open(filename, 'w', encoding='utf-8') as f:
f.write(self.report_text.toHtml())
QMessageBox.information(self, '成功', f'报告已保存至:\n{filename}')
except Exception as e:
QMessageBox.critical(self, '错误', f'保存失败:\n{str(e)}')
def export_charts(self):
"""导出图表"""
if not self.charts:
QMessageBox.warning(self, '提示', '请先进行计算')
return
dirname = QFileDialog.getExistingDirectory(self, '选择保存目录')
if dirname:
try:
charts_to_export = [
('投资仪表盘', self.charts.create_metrics_dashboard),
('成本结构', self.charts.create_cost_structure_chart),
('月度预测', self.charts.create_monthly_projection_chart),
('雷达图', self.charts.create_radar_chart),
('回本周期', self.charts.create_break_even_chart),
]
for name, func in charts_to_export:
fig = func()
fig.savefig(f'{dirname}/{name}_{datetime.now().strftime("%Y%m%d")}.png',
dpi=150, bbox_inches='tight')
plt.close(fig)
QMessageBox.information(self, '成功', f'图表已保存至:\n{dirname}')
except Exception as e:
QMessageBox.critical(self, '错误', f'导出失败:\n{str(e)}')
def show_about(self):
"""关于"""
QMessageBox.about(self, '关于',
'<h3>投资计算评估系统 v2.0</h3>'
'<p>专业的投资评估工具</p>'
'<p>集成夏普比率、索提诺比率、特雷诺比率等专业指标</p>'
'<p>帮助投资者进行全面的项目评估</p>')
# ============================================================================
# 主程序入口
# ============================================================================
def main():
app = QApplication(sys.argv)
# 设置应用样式
app.setStyle('Fusion')
# 创建并显示主窗口
window = InvestmentApp()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
5. 总结
实现逻辑,其核心价值在于:
- 实体投资的量化金融化:它没有停留在"赚了多少钱",而是引入了标准差(波动率)、贝塔系数(系统风险)来评估实体店铺的稳定性。
- 基准化对比:内置了餐饮、零售等多个行业的基准数据库,使得评估结果不是孤立的数字,而是相对于行业水平的相对评分。
- 全维度视图:覆盖了从"生存(回本)"到"健康(现金流质量)"再到"效率(坪效/人效)"的完整闭环。