185 lines
5.9 KiB
Python
185 lines
5.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
动态保险定价模型
|
||
基于精算原理,结合六维风险评分实现"千企千面"费率计算
|
||
"""
|
||
import logging
|
||
import sys
|
||
import os
|
||
|
||
# 将项目根目录添加到 Python 路径
|
||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
|
||
from config import INSURANCE_PRODUCTS
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
# 行业风险系数
|
||
INDUSTRY_RISK_FACTORS = {
|
||
"芯片": 1.35, # 地缘风险+技术迭代双高
|
||
"AI": 1.30, # 合规风险+技术竞争
|
||
"软件": 1.05, # 相对成熟
|
||
"医疗器械": 0.95, # 技术壁垒高但风险较稳
|
||
"新能源": 1.15, # 技术路线之争+贸易摩擦
|
||
"消费电子": 1.10, # 供应链风险
|
||
}
|
||
|
||
# 企业规模折扣(大企业风险分散能力更强)
|
||
SCALE_DISCOUNT = {
|
||
"超大型": 0.85, # 营收 > 500亿
|
||
"大型": 0.90, # 100-500亿
|
||
"中型": 1.00, # 10-100亿
|
||
"小型": 1.15, # 1-10亿
|
||
"微型": 1.30, # < 1亿
|
||
}
|
||
|
||
|
||
def get_enterprise_scale(revenue: float) -> str:
|
||
"""根据营收判断企业规模"""
|
||
if revenue >= 50_000_000_000:
|
||
return "超大型"
|
||
elif revenue >= 10_000_000_000:
|
||
return "大型"
|
||
elif revenue >= 1_000_000_000:
|
||
return "中型"
|
||
elif revenue >= 100_000_000:
|
||
return "小型"
|
||
return "微型"
|
||
|
||
|
||
def calculate_premium(
|
||
product_key: str,
|
||
comprehensive_risk_score: int,
|
||
six_dimension_scores: dict,
|
||
sector: str = "",
|
||
revenue: float = 0,
|
||
) -> dict:
|
||
"""
|
||
动态费率计算器
|
||
|
||
定价公式:
|
||
实际保费 = 基础保费 × 风险系数 × 行业调整 × 规模折扣
|
||
|
||
风险系数由综合评分决定(分段线性):
|
||
- [0, 30) → 0.7 ~ 0.9(优质折扣)
|
||
- [30, 50) → 0.9 ~ 1.2(标准浮动)
|
||
- [50, 70) → 1.2 ~ 1.8(风险上浮)
|
||
- [70, 90) → 1.8 ~ 2.5(惩罚性费率)
|
||
- [90,100] → 拒保或附加极高免赔额
|
||
"""
|
||
product = INSURANCE_PRODUCTS.get(product_key)
|
||
if not product:
|
||
return {"error": f"未知保险产品: {product_key}"}
|
||
|
||
base_premium = product["base_premium"]
|
||
base_coverage = product["base_coverage"]
|
||
|
||
# 1. 计算风险系数
|
||
risk_multiplier = _calculate_risk_multiplier(comprehensive_risk_score)
|
||
|
||
# 2. 行业调整系数
|
||
industry_factor = INDUSTRY_RISK_FACTORS.get(sector, 1.0)
|
||
|
||
# 3. 规模折扣
|
||
scale = get_enterprise_scale(revenue)
|
||
scale_factor = SCALE_DISCOUNT[scale]
|
||
|
||
# 4. 特定险种的维度调整
|
||
dimension_adjustment = _get_dimension_adjustment(product_key, six_dimension_scores)
|
||
|
||
# 5. 最终保费
|
||
final_premium = base_premium * risk_multiplier * industry_factor * scale_factor * dimension_adjustment
|
||
|
||
# 6. 核保条件
|
||
deductible_rate = _calculate_deductible(comprehensive_risk_score)
|
||
adjusted_coverage = base_coverage * (1.0 if comprehensive_risk_score < 70 else 0.7)
|
||
|
||
return {
|
||
"product_name": product["name"],
|
||
"product_key": product_key,
|
||
"base_premium": base_premium,
|
||
"base_coverage": base_coverage,
|
||
"risk_multiplier": round(risk_multiplier, 3),
|
||
"industry_factor": round(industry_factor, 3),
|
||
"scale_factor": round(scale_factor, 3),
|
||
"scale_label": scale,
|
||
"dimension_adjustment": round(dimension_adjustment, 3),
|
||
"final_premium": round(final_premium, 2),
|
||
"adjusted_coverage": round(adjusted_coverage, 2),
|
||
"deductible_rate": round(deductible_rate, 3),
|
||
"deductible_amount": round(adjusted_coverage * deductible_rate, 2),
|
||
"comprehensive_risk_score": comprehensive_risk_score,
|
||
"is_insurable": comprehensive_risk_score < 90,
|
||
"pricing_breakdown": (
|
||
f"¥{base_premium:,.0f} × {risk_multiplier:.2f}(风险) "
|
||
f"× {industry_factor:.2f}(行业) × {scale_factor:.2f}(规模) "
|
||
f"× {dimension_adjustment:.2f}(维度) = ¥{final_premium:,.0f}"
|
||
),
|
||
}
|
||
|
||
|
||
def _calculate_risk_multiplier(score: int) -> float:
|
||
"""分段线性风险系数计算"""
|
||
if score < 30:
|
||
return 0.7 + (score / 30) * 0.2 # 0.7 ~ 0.9
|
||
elif score < 50:
|
||
return 0.9 + ((score - 30) / 20) * 0.3 # 0.9 ~ 1.2
|
||
elif score < 70:
|
||
return 1.2 + ((score - 50) / 20) * 0.6 # 1.2 ~ 1.8
|
||
elif score < 90:
|
||
return 1.8 + ((score - 70) / 20) * 0.7 # 1.8 ~ 2.5
|
||
else:
|
||
return 3.0 # 拒保级别
|
||
|
||
|
||
def _get_dimension_adjustment(product_key: str, scores: dict) -> float:
|
||
"""针对特定险种,根据相关维度得分进行微调"""
|
||
if product_key == "ip_lawsuit":
|
||
# 知识产权被诉险:重点看技术路线和专利
|
||
tech_score = scores.get("tech_disruption", 50)
|
||
return 0.8 + (tech_score / 100) * 0.4 # 0.8 ~ 1.2
|
||
|
||
elif product_key == "exec_departure":
|
||
# 高管离职险:重点看人员流失风险
|
||
talent_score = scores.get("talent_loss", 50)
|
||
return 0.7 + (talent_score / 100) * 0.6 # 0.7 ~ 1.3
|
||
|
||
elif product_key == "data_compliance":
|
||
# 数据合规险:重点看合规风险
|
||
compliance_score = scores.get("algo_compliance", 50)
|
||
return 0.8 + (compliance_score / 100) * 0.4 # 0.8 ~ 1.2
|
||
|
||
return 1.0
|
||
|
||
|
||
def _calculate_deductible(score: int) -> float:
|
||
"""计算免赔率"""
|
||
if score < 30:
|
||
return 0.05 # 5%
|
||
elif score < 50:
|
||
return 0.10 # 10%
|
||
elif score < 70:
|
||
return 0.15 # 15%
|
||
elif score < 90:
|
||
return 0.25 # 25%
|
||
else:
|
||
return 0.50 # 50%(惩罚性高免赔)
|
||
|
||
|
||
def calculate_all_products(
|
||
comprehensive_risk_score: int,
|
||
six_dimension_scores: dict,
|
||
sector: str = "",
|
||
revenue: float = 0,
|
||
) -> list:
|
||
"""计算所有险种的保费"""
|
||
results = []
|
||
for product_key in INSURANCE_PRODUCTS:
|
||
result = calculate_premium(
|
||
product_key, comprehensive_risk_score,
|
||
six_dimension_scores, sector, revenue,
|
||
)
|
||
results.append(result)
|
||
return results
|