guzhi/app/utils/calculation_engine/formula_registry.py

279 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
公式元数据注册表
用于将计算引擎中的每个公式节点(含子公式)映射到唯一的 code、名称、公式说明以及排序
以便在 valuation_calculation_steps 表中进行结构化记录,并最终生成可读的计算报告。
"""
from __future__ import annotations
from dataclasses import dataclass
from decimal import Decimal
from typing import Dict, List, Optional
@dataclass(frozen=True)
class FormulaMeta:
code: str
name: str
formula: str
order: Decimal
parent_code: Optional[str]
group_code: str
FormulaTreeNode = Dict[str, object]
def _node(
code: str,
name: str,
formula: str,
order: str,
children: Optional[List[FormulaTreeNode]] = None,
group: Optional[str] = None,
) -> FormulaTreeNode:
return {
"code": code,
"name": name,
"formula": formula,
"order": order,
"group_code": group,
"children": children or [],
}
FORMULA_TREE: List[FormulaTreeNode] = [
_node(
"FINAL_A",
"最终估值A",
"最终估值A = 模型估值B × 0.7 + 市场估值C × 0.3",
"10",
children=[
_node(
"MODEL_B",
"模型估值B",
"模型估值B = 经济价值B1 × 0.7 + 文化价值B2 × 0.3 × 风险调整系数B3",
"20",
group="MODEL_B",
children=[
_node(
"MODEL_B_ECON_B1",
"经济价值B1",
"经济价值B1 = 基础价值B11 × (1 + 流量因子B12) × 政策乘数B13",
"21",
children=[
_node(
"MODEL_B_ECON_B11",
"基础价值B11",
"基础价值B11 = 财务价值F × (0.45 + 0.05 × 行业系数I) + 法律强度L × (0.35 + 0.05 × 行业系数I) + 发展潜力D × 0.2",
"21.1",
children=[
_node(
"MODEL_B_ECON_B11_FINANCIAL_VALUE",
"财务价值F",
"财务价值F = [3年内年均收益 × (1 + 增长率)^5] ÷ 5",
"21.11",
),
_node(
"MODEL_B_ECON_B11_LEGAL_STRENGTH",
"法律强度L",
"法律强度L = 专利分 × 0.4 + 普及分 × 0.3 + 侵权分 × 0.3",
"21.12",
),
_node(
"MODEL_B_ECON_B11_DEVELOPMENT_POTENTIAL",
"发展潜力D",
"发展潜力D = 专利分 × 0.5 + ESG分 × 0.2 + 创新投入比 × 0.3",
"21.13",
),
_node(
"MODEL_B_ECON_B11_INDUSTRY_COEFFICIENT",
"行业系数I",
"行业系数I = (目标行业平均ROE - 基准行业ROE) ÷ 基准行业ROE",
"21.14",
),
],
),
_node(
"MODEL_B_FLOW_B12",
"流量因子B12",
"流量因子B12 = ln(S1 ÷ S2) × 0.3 + 社交媒体传播度S3 × 0.7",
"21.2",
children=[
_node(
"MODEL_B_FLOW_B12_INTERACTION_INDEX",
"互动量指数",
"互动量指数 = (点赞 + 评论 + 分享) ÷ 1000",
"21.21",
),
_node(
"MODEL_B_FLOW_B12_COVERAGE_INDEX",
"覆盖人群指数",
"覆盖人群指数 = 粉丝数 ÷ 10000",
"21.22",
),
_node(
"MODEL_B_FLOW_B12_CONVERSION_EFFICIENCY",
"转化效率",
"转化效率 = 商品链接点击量 ÷ 内容浏览量",
"21.23",
),
_node(
"MODEL_B_FLOW_B12_SOCIAL_SPREAD",
"社交媒体传播度S3",
"社交媒体传播度S3 = 互动量指数 × 0.4 + 覆盖人群指数 × 0.3 + 转化效率 × 0.3",
"21.24",
),
],
),
_node(
"MODEL_B_POLICY_B13",
"政策乘数B13",
"政策乘数B13 = 1 + 政策契合度评分P × 0.15,其中 P = 政策匹配度 × 0.4 + 实施阶段评分 × 0.3 + 资金支持度 × 0.3",
"21.3",
),
],
),
_node(
"MODEL_B_CULTURAL_B2",
"文化价值B2",
"文化价值B2 = 活态传承系数B21 × 0.6 + (纹样基因值B22 ÷ 10) × 0.4",
"22",
children=[
_node(
"MODEL_B_CULTURAL_B21",
"活态传承系数B21",
"活态传承系数B21 = 传承人等级系数 × 0.4 + 教学传播频次 × 0.3 + 跨界合作深度 × 0.3",
"22.1",
children=[
_node(
"MODEL_B_CULTURAL_B21_TEACHING_FREQ",
"教学传播频次",
"教学传播频次 = 线下传习次数 × 0.6 + 线上课程点击量(万) × 0.4",
"22.11",
),
],
),
_node(
"MODEL_B_CULTURAL_B22",
"纹样基因值B22",
"纹样基因值B22 = (结构复杂度SC × 0.6 + 归一化信息熵H × 0.4) × 历史传承度HI × 10",
"22.2",
),
],
),
_node(
"MODEL_B_RISK_B3",
"风险调整系数B3",
"风险调整系数B3 = 0.8 + 风险评分总和R × 0.4,其中 R = 市场风险 × 0.3 + 法律风险 × 0.4 + 传承风险 × 0.3",
"23",
children=[
_node(
"MODEL_B_RISK_B3_MARKET",
"市场风险",
"市场风险依据价格波动率:波动率 ≤5% 计10分5-15%计5分>15%计0分",
"23.1",
),
_node(
"MODEL_B_RISK_B3_LEGAL",
"法律风险",
"法律风险根据诉讼状态评分(无诉讼/已解决/未解决)",
"23.2",
),
_node(
"MODEL_B_RISK_B3_INHERITANCE",
"传承风险",
"传承风险依据传承人年龄≤50岁10分50-70岁5分>70岁0分取最高分",
"23.3",
),
],
),
],
),
_node(
"MARKET_C",
"市场估值C",
"市场估值C = 市场竞价C1 × 热度系数C2 × 稀缺性乘数C3 × 时效性衰减C4",
"30",
group="MARKET_C",
children=[
_node(
"MARKET_C_C1",
"市场竞价C1",
"市场竞价C1 结合历史交易价格、人工竞价与专家估值的加权结果",
"30.1",
),
_node(
"MARKET_C_C2",
"热度系数C2",
"热度系数C2 = 1 + 浏览热度分(依据日均浏览量与收藏数量)",
"30.2",
),
_node(
"MARKET_C_C3",
"稀缺性乘数C3",
"稀缺性乘数C3 = 1 + 稀缺等级分",
"30.3",
),
_node(
"MARKET_C_C4",
"时效性衰减C4",
"时效性衰减C4 依据距最近市场活动天数的衰减系数",
"30.4",
),
],
),
_node(
"DYNAMIC_PLEDGE_RATE",
"动态质押率DPR",
"动态质押率DPR = 基础质押率 × (1 + 流量修正系数) + 政策加成系数 - 流动性调节因子",
"40",
group="DYNAMIC_PLEDGE",
),
],
)
]
def _build_index() -> Dict[str, FormulaMeta]:
index: Dict[str, FormulaMeta] = {}
def dfs(nodes: List[FormulaTreeNode], parent_code: Optional[str], group_code: Optional[str]):
for node in nodes:
code = node["code"]
name = node["name"]
formula = node["formula"]
order = Decimal(str(node["order"]))
explicit_group = node.get("group_code")
if explicit_group:
current_group = explicit_group
elif parent_code is None:
current_group = code
else:
current_group = group_code or parent_code
meta = FormulaMeta(
code=code,
name=name,
formula=formula,
order=order,
parent_code=parent_code,
group_code=current_group,
)
index[code] = meta
dfs(node.get("children", []), code, current_group)
dfs(FORMULA_TREE, None, None)
return index
FORMULA_INDEX: Dict[str, FormulaMeta] = _build_index()
def get_formula_meta(code: str) -> FormulaMeta:
meta = FORMULA_INDEX.get(code)
if not meta:
raise KeyError(f"公式编码未注册: {code}")
return meta