目录
核心目标
准确的销量预测和智能的补货策略最终都服务于一个核心目标:合理的备货量,而备货量的计算,除了预测值之外,一个至关重要的组成部分是安全库存。
在舆情风险驱动的需求波动环境中,传统的固定安全库存方法已经失效。本方案采用动态安全库存(DSS, Dynamic Safety Stock) 的方法,综合考虑以下因素:
核心公式:
最终备货量 = 预测销售量 + 动态安全库存
= 预测销售量 + 风险调整系数 × 需求变异系数
其中风险调整系数包括:
├─ 舆情压力指数 (LDI/CRI/GRI/FRI)
├─ 商品特性 (易腐性/季节性)
├─ 供应链可靠性 (交期稳定性)
└─ 采购商等级 (信用风险)
关键名词解释
DDMRP涉及多个专业术语,理解这些概念对正确应用系统至关重要:
「净流位置」(Net Flow Position, NFP)
- 通俗理解:"现在库里有多少货可以用"
- 精确定义:当前库存 - 已承诺订单量(下游已下单但尚未收货)
- 计算式:NFP = 现有库存 - 待履行订单量
- 为什么重要:不能只看库存数字,因为订单已经"承诺"给客户了,不能再用
- 举例:库里有100单位,但有60单位已经被下游订单承诺,实际可用只有40单位
「解耦提前期」(Decoupling Lead Time, DLT)
- 通俗理解:"从下单到收货要等几天"
- 精确定义:从向供应商下单,到物料到达本地仓库的总时间(包括采购、生产、运输、清关等)
- 包含内容:采购确认(1天) + 生产制造(5天) + 运输配送(3天) + 接收入库(1天) = 10天
- 为什么重要:这个周期越长,库存缓冲需求就越大
- ABC-XYZ体现:不同供应商DLT不同(长期供应商短,新供应商长)
「可变性系数」(Variability Factor, VF)
- 通俗理解:"需求有多不稳定"
- 精确定义:用来放大安全库存的系数,反映需求波动性和预测误差范围
- 取值范围:0.15 ~ 0.40(DDMRP推荐)
- VF越低 → 需求越稳定 → 缓冲库存越少(如AX等级 VF=0.15)
- VF越高 → 需求越波动 → 缓冲库存越多(如BZ等级 VF=0.40)
- 计算基础:结合历史需求标准差 + 预测误差率
- 舆情调整:GRI升高(地缘风险增加需求激增风险) → VF自动增大
- 举例:平均日用10单位,DLT=10天,VF=0.20
- 红区基础 = 10 × 10 = 100单位
- 红区安全 = 100 × 0.20 = 20单位
- 红区总计 = 120单位(这是最低库存警戒线)
「提前期系数」(Lead Time Factor, LTF)
- 通俗理解:"供应方有多不可靠"
- 精确定义:用来调整绿区(监控缓冲)的系数,反映交期不确定性和供应风险
- 取值范围:0.20 ~ 0.45(DDMRP推荐)
- LTF越低 → 供应越可靠 → 监控库存越少(如AX等级 LTF=0.20)
- LTF越高 → 供应越不可靠 → 监控库存越多(如BZ等级 LTF=0.45)
- 风险源:供应商交期延迟(LDI) + 合规风险(CRI) + 汇率波动(FRI)
- 舆情调整:LDI升高(物流风险) → LTF自动增大 → 绿区自动扩大
- ABC体现:A类高价值商品LTF更低(可靠供应才能保证销售),C类低价值LTF相对较高(成本控制优先)
ABC-XYZ矩阵商品分类与补货策略
大宗商品应采用ABC-XYZ二维矩阵分类模型,而非简单的单维ABC分类。
- ABC维度(价值维度):基于商品价值,决定库存投入优先级
- XYZ维度(需求维度):基于需求稳定性,决定预测方法和补货规则
ABC分类维度(基于价值 / 销售额)
| 等级 | 特征 | 占比 | 库存投入 |
|---|---|---|---|
| A类 | 高价值、需求稳定 | 销售额80% | 库存成本优化、严格周转 |
| B类 | 中等价值、需求中等 | 销售额15% | 适度库存、定期监控 |
| C类 | 低价值、滞销品 | 销售额5% | 最小库存、尽快清货 |
XYZ分类维度(基于需求波动性)
| 等级 | 需求特征 | 需求系数 | 预测难度 | 补货策略 |
|---|---|---|---|---|
| X类 | 需求稳定、波动小 | CV<0.2 | 容易 | 定期推式MRP、固定安全库存 |
| Y类 | 需求波动、间断性 | 0.2<CV<0.6 | 中等 | Croston法、聚合分解、动态缓冲 |
| Z类 | 需求剧烈、不稳定 | CV>0.6 | 困难 | 采用最小库存、按单订购(MTO) |
ABC-XYZ九宫格矩阵详解
第一象限:A类商品(高价值)
AX:高价值,需求稳定
- 核心利润来源、需求稳定、预测准确高
- 策略:降低安全库存、释放营资金、提高资金周转效率
- 补货规则:MRP系统、定期推式、库存周转>6次/年
- 库存目标:绝对值最小化、周转速度最大化
- 舆情敏感性:低(风险调整系数 0.10-0.15)
AY:高价值,需求波动
- 价值高但需求存在波动性、预测难度中等
- 策略:平衡库存制、适度安全库存、定期监控
- 补货规则:Croston法与聚合分解、DDMRP缓冲、定期审视
- 库存目标:在保持服务水平与成本间平衡
- 舆情敏感性:中(风险调整系数 0.25-0.35)
AZ:高价值,需求剧烈波动
- 价值高但需求极不稳定、预测难度大
- 策略:采用保守库存、需求信号制、密切跟踪市场
- 补货规则:采用订单驱动(MTO/ATO)、减少库存压力、关注市场动态
- 库存目标:库存额固定、减少资金占压、快速响应
- 舆情敏感性:高(风险调整系数 0.40-0.50)
第二象限:B类商品(中等价值)
BX:中等价值,需求稳定
- 价值中等、需求稳定可预测
- 策略:适度控制、定期盘点、平衡管理
- 补货规则:定期MRP、定期购、平衡库存
- 库存目标:库存水平与周转率都在中等水平
- 舆情敏感性:低-中(风险调整系数 0.15-0.25)
BY:中等价值,需求波动
- 价值中等、需求存在波动性
- 策略:Croston法与聚合分解、定期监控
- 补货规则:Croston法与聚合分解、定期监控
- 库存目标:加强控制、适度安全库存、避免缺货
- 舆情敏感性:中(风险调整系数 0.30-0.40)
BZ:中等价值,需求剧烈波动
- 价值中等但需求波动剧烈
- 策略:加强控制、适度安全库存、避免缺货
- 补货规则:Croston法与聚合分解、定期监控
- 库存目标:降低库存、增强控制、适度安全库存
- 舆情敏感性:中-高(风险调整系数 0.35-0.50)
第三象限:C类商品(低价值)
CX:低价值,需求稳定
- 价值贵最小、需求稳定
- 策略:自动化补充、以最低管理成本保保供应
- 补货规则:自动化补货、以最低管理成本保保供应
- 库存目标:最小库存、自动补充、防止缺货
- 舆情敏感性:极低(风险调整系数 <0.10)
CY:低价值,需求波动
- 价值贵最小、需求波动
- 策略:简化管理、降低持有成本
- 补货规则:简化管理、降低持有成本
- 库存目标:尽量避免有库存、采用按订单生产/采购模式
- 舆情敏感性:极低(风险调整系数 <0.10)
CZ:低价值,需求剧烈波动
- 价值贵最小、需求波动剧烈
- 策略:尽量避免有库存、采用按订单生产/采购模式
- 补货规则:尽量避免有库存、采用按订单生产/采购模式
- 库存目标:零库存理想、按实际需求采购
- 舆情敏感性:极低(风险调整系数 <0.08)
商品分类的量化标准
def classify_commodity_abc_xyz(
annual_sales_value: float, # 年销售额(¥)
total_category_value: float, # 同类产品总销售额(¥)
demand_series: list, # 历史需求序列(最近90天日销)
sentiment_indices: dict = None # 舆情指标
) -> dict:
"""
基于ABC-XYZ矩阵对商品进行二维分类
"""
# ABC分类:基于销售额占比
sales_ratio = annual_sales_value / total_category_value
if sales_ratio >= 0.80: # 前80%销售额
abc_grade = 'A'
elif sales_ratio >= 0.95: # 80%-95%
abc_grade = 'B'
else: # 95%以后
abc_grade = 'C'
# XYZ分类:基于需求变异系数(Coefficient of Variation)
demand_mean = np.mean(demand_series)
demand_std = np.std(demand_series)
cv = demand_std / demand_mean if demand_mean > 0 else float('inf')
if cv < 0.2:
xyz_grade = 'X' # 稳定
elif cv < 0.6:
xyz_grade = 'Y' # 波动
else:
xyz_grade = 'Z' # 剧烈波动
# 综合分类
combined_grade = abc_grade + xyz_grade
# 根据舆情调整分类
if sentiment_indices:
gri = sentiment_indices.get('gri', 0) # 地缘政治风险
cri = sentiment_indices.get('cri', 0) # 合规风险
# 高风险环境下,提升波动等级
if (gri > 6 or cri > 6) and xyz_grade == 'X':
xyz_grade = 'Y' # X升级到Y
elif (gri > 7 or cri > 7) and xyz_grade == 'Y':
xyz_grade = 'Z' # Y升级到Z
combined_grade = abc_grade + xyz_grade
return {
'combined_grade': combined_grade,
'abc_grade': abc_grade,
'xyz_grade': xyz_grade,
'sales_ratio': round(sales_ratio, 4),
'cv': round(cv, 3),
'classification_details': {
'abc_logic': f"销售额占比{sales_ratio*100:.1f}% → {abc_grade}类",
'xyz_logic': f"需求变异系数CV={cv:.3f} → {xyz_grade}类",
'priority': _get_priority(combined_grade),
'management_intensity': _get_intensity(combined_grade)
}
}
def _get_priority(grade: str) -> str:
"""获取管理优先级"""
priority_map = {
'AX': 'HIGHEST', # 最高优先级
'AY': 'HIGH',
'AZ': 'HIGH',
'BX': 'MEDIUM',
'BY': 'MEDIUM',
'BZ': 'MEDIUM',
'CX': 'LOW',
'CY': 'LOW',
'CZ': 'LOWEST' # 最低优先级
}
return priority_map.get(grade, 'UNKNOWN')
def _get_intensity(grade: str) -> str:
"""获取管理强度描述"""
intensity_map = {
'AX': '严格库存优化、周转最大化',
'AY': '平衡库存制、定期审视',
'AZ': '订单驱动、快速响应',
'BX': '适度控制、定期盘点',
'BY': '强化控制、防止缺货',
'BZ': '降低库存、增强控制',
'CX': '自动补充、最小成本',
'CY': '简化管理、降低成本',
'CZ': '零库存、按单采购'
}
return intensity_map.get(grade, '未知')
舆情系统与补货的数据融合
基于ABC-XYZ矩阵分类,舆情指标需要动态调整不同类商品的补货参数:
# 舆情驱动的补货量调整
def calculate_sentiment_adjusted_replenishment_abc_xyz(
base_forecast: float, # 基础预测值
sentiment_indices: dict, # 舆情指标 {ldi, cri, gri, fri}
commodity_grade: str, # ABC-XYZ纵模(如'AX', 'BY', 'CZ')
safety_stock: float # 计算出的安全库存
) -> dict:
"""
基于ABC-XYZ数据驱动的补货量计算
"""
ldi = sentiment_indices.get('ldi', 0) / 10.0 # 物流延迟指数(0-1)
cri = sentiment_indices.get('cri', 0) / 10.0 # 合规风险指数(0-1)
gri = sentiment_indices.get('gri', 0) / 10.0 # 地缘政治指数(0-1)
fri = sentiment_indices.get('fri', 0) / 10.0 # 金融风险指数(0-1)
# 步骤1: 计算舆情压力指数
sentiment_pressure = (
0.25 * ldi + # 物流延迟直接影响补货交期
0.20 * cri + # 合规风险影响成本结构
0.35 * gri + # 地缘政治风险驱动采购行为(权重最高)
0.20 * fri # 金融风险影响订单确定性
)
# 步骤2: ABC-XYZ矩阵中,不同的商品有不同的风险敏感性
# ABC维度(A/B/C)决定库存成本敏感性
# XYZ维度(X/Y/Z)决定需求波动敏感性
risk_sensitivity_map = {
# A类:高价值,低成本敏感性
'AX': 0.10, # 高价值(低效合,低波动)
'AY': 0.25, # 中高价值、中波动
'AZ': 0.40, # 高价值、高波动、平衡库存
# B类:中等价值,中等成本敏感性
'BX': 0.15, # 中低价值、稳定需求
'BY': 0.35, # 中低价值、中波动需求
'BZ': 0.50, # 中低价值、高波动需求
# C类:低价值,低成本敏感性(极低需求)
'CX': 0.08, # 低价值、稳定需求
'CY': 0.10, # 低价值、中波动
'CZ': 0.12 # 低价值、高波动、最小库存
}
risk_multiplier = risk_sensitivity_map.get(commodity_grade, 0.2)
risk_adjusted = 1.0 + (sentiment_pressure * risk_multiplier)
# 步骤3: 计算调整后的安全库存
adjusted_safety_stock = safety_stock * risk_adjusted
# 步骤4: 计算最终补货量
# GRI触发的需求激增预期
demand_surge_multiplier = 1.0
# ABC-XYZ不同等级的GRI敦值不同
if commodity_grade in ['AX', 'AY', 'BX']: # 低风险哦值
gri_threshold = 0.75
gri_factor = 0.4
elif commodity_grade in ['AZ', 'BY', 'BZ', 'CZ']: # 中高风险哦值
gri_threshold = 0.50
gri_factor = 0.6
else: # C类低价值商品不调整GRI
gri_threshold = 0.9
gri_factor = 0.2
if gri > gri_threshold:
# 高地缘政治风险,预期采购商囊积行为
demand_surge_multiplier = 1.0 + (gri - gri_threshold) * gri_factor
final_replenishment = (
base_forecast * demand_surge_multiplier + adjusted_safety_stock
)
return {
'commodity_grade': commodity_grade,
'base_forecast': round(base_forecast, 2),
'sentiment_pressure_score': round(sentiment_pressure, 3),
'risk_multiplier': risk_multiplier,
'adjusted_safety_stock': round(adjusted_safety_stock, 2),
'demand_surge_multiplier': round(demand_surge_multiplier, 3),
'final_replenishment_qty': round(final_replenishment, 2),
'adjustment_details': {
'ldi_impact': f"物流延迟{ldi:.1f}: {'{交期风险高}' if ldi > 0.5 else '交期正常'}",
'gri_impact': f"地缘风险{gri:.1f}: {'{挀幝可能}' if gri > 0.5 else '低风险'}",
'cri_impact': f"合规风险{cri:.1f}: {'{成本压力高}' if cri > 0.6 else '浅水低'}",
'fri_impact': f"金融风险{fri:.1f}: {'{供应商认信兴趣高}' if fri > 0.5 else '供应商夷体'}",
'replenishment_strategy': _get_replenishment_strategy(commodity_grade, sentiment_pressure)
}
}
def _get_replenishment_strategy(grade: str, pressure: float) -> str:
"""根据商品等级和舆情压力订制补货策略"""
if grade.startswith('A'):
return '优先保供、优先收款、严格控制资金'
elif grade.startswith('B'):
if grade.endswith('X'):
return '正常补货、适度安全库'
else:
return '优先补货、一接一订'
else: # C类
return '低优先级、按需采购、清货业务'
不同安全库存方法的对比
静态安全库存 vs 动态安全库存
| 特性 | 静态安全库存 (Static SS) | 动态安全库存 (DSS) |
|---|---|---|
| 定义 | 基于历史平均值的固定库存缓冲。 | 基于实时数据和市场信号的自适应库存策略。 |
| 计算方法 | 简单公式 (如2%规则) 和历史数据。 | 实时数据流、算法、机器学习和统计分析。 |
| 更新频率 | 按季度或年度进行审查。 | 基于当前条件持续进行实时校准。 |
| 市场响应 | 滞后被动。 | 积极主动。 |
| 主要用例 | 市场稳定、SKU组合有限。 | 市场波动、易损产品、复杂供应链和大型SKU组合。 |
| 优势 | 实施简单高效。 | 减少缺货和过剩库存,提高服务水平和成本效益。 |
| 劣势 | 波动期易丧失效果或导致库存过剩风险。 | 需要对技术和培训进行投资。 |
动态安全库存的核心计算
动态安全库存的计算需要结合以下关键因素:
def calculate_dss_comprehensive(
demand_series: np.ndarray, # 历史需求时间序列
forecast_errors: np.ndarray, # 预测误差历史
lead_time_days: int, # 交期时间
sentiment_pressure: float, # 舆情压力指数 (0-1)
commodity_grade: str, # A/B/C等级
service_level: float = 0.95 # 服务水平
) -> dict:
"""
流程、预测误差和舆情指标综合动态安全库存计算。
核心公式:
DSS = Z×σ_combined×√L + 舆情调整
其中:
- Z: 服务水平系数
- σ_combined: 组合的不确定性 (需求+执行)
- L: 交期平方开方
- 舆情调整: 基于风险指数
"""
# 1. 计算需求波动 (标准差)
demand_std = np.std(demand_series)
demand_mean = np.mean(demand_series)
# 2. 计算执行不确定 (预测误差)
execution_uncertainty = np.std(forecast_errors)
# 3. 组合不确定性 (综合不确定)
combined_uncertainty = np.sqrt(
demand_std**2 + execution_uncertainty**2
)
# 4. 服务水平z值
z_scores = {0.90: 1.28, 0.95: 1.645, 0.99: 2.33}
z = z_scores.get(service_level, 1.645)
# 5. 基础DSS
base_dss = z * combined_uncertainty * np.sqrt(lead_time_days)
# 6. 舆情调整 (不同等级的敏感性不同)
sentiment_multiplier = {
'A': 0.10, # A类低敏感
'B': 0.35, # B类中敏感
'C': 0.05 # C类极低敏感
}
sentiment_buffer = sentiment_pressure * base_dss * sentiment_multiplier.get(commodity_grade, 0.15)
dss = base_dss + sentiment_buffer
return {
'dss': round(dss, 2),
'base_dss': round(base_dss, 2),
'sentiment_buffer': round(sentiment_buffer, 2),
'combined_uncertainty': round(combined_uncertainty, 2),
'demand_std': round(demand_std, 2),
'execution_uncertainty': round(execution_uncertainty, 2)
}
DDMRP模型与B等商品的具体应用
DDMRP概述
需求驱动的物料需求计划 (DDMRP) 是一个前沿的及时补货方法,基于"拉动"的库存策略。与传统的基于预测的"推动"MRP方法形成了明显对照。
DDMRP的核心与其"定位、保护、拉动"样式形成的三个工作流程:
- 定位 (Position):在物料清单 (BOM) 中的关键路径物料或组件处设置库存缓冲。
- 保护 (Protect):利用安全库存来吸收需求和供应的波动,保证生产计划。
- 拉动 (Pull):根据实际需求和库存水平,动态地拉动供给,而非根据固定的预测计划。
DDMRP缓冲区的ABC-XYZ分级配置
缓冲区配置是DDMRP的核心,其VF、LTF参数必须根据商品所处的ABC-XYZ等级进行分级设置。不同等级的基础参数和舆情敏感性差异巨大:
缓冲区参数的ABC-XYZ分级矩阵
ABC-XYZ等级与VF/LTF基础值对应表:
VF(可变性系数) LTF(提前期系数)
需求波动容限 交期不确定性
AX 0.15 → AX 0.20 (最低保守) (高可靠)
AY 0.25 AY 0.30
AZ 0.35 AZ 0.40
BX 0.20 BX 0.25
BY 0.30 BY 0.35
BZ 0.40 BZ 0.45 (最激进)
CX 0.15 CX 0.20
CY 0.25 CY 0.30
CZ 0.20 CZ 0.25 (C类简化管理)
策略原则:
- A类商品 VF/LTF最低:价值大,不容许频繁缺货或过量库存
- B类商品 VF/LTF中等:平衡成本与服务水平
- C类商品 VF/LTF特殊:低价值,更注重成本控制而非精细管理
- XYZ维度 按波动性线性增加:X<Y<Z,体现需求稳定性差异
ABC-XYZ适配的DDMRP缓冲区计算代码
def calculate_ddmrp_buffer_parameters_abc_xyz(
adu: float, # 平均日用量
dlt: int, # 解耦提前期 (天)
commodity_grade: str, # ABC-XYZ等级 ('AX','BY','CZ'等)
sentiment_indices: dict = None, # 舆情指标
) -> dict:
"""
基于ABC-XYZ矩阵的DDMRP缓冲区参数计算
"""
# 步骤1: ABC-XYZ基础VF/LTF矩阵
vf_matrix = {
'AX': 0.15, 'AY': 0.25, 'AZ': 0.35,
'BX': 0.20, 'BY': 0.30, 'BZ': 0.40,
'CX': 0.15, 'CY': 0.25, 'CZ': 0.20
}
ltf_matrix = {
'AX': 0.20, 'AY': 0.30, 'AZ': 0.40,
'BX': 0.25, 'BY': 0.35, 'BZ': 0.45,
'CX': 0.20, 'CY': 0.30, 'CZ': 0.25
}
vf = vf_matrix.get(commodity_grade, 0.25)
ltf = ltf_matrix.get(commodity_grade, 0.30)
# 步骤2: 舆情驱动的动态调整(分等级调整幅度)
if sentiment_indices:
gri = sentiment_indices.get('gri', 0) / 10 # 0-1
ldi = sentiment_indices.get('ldi', 0) / 10
cri = sentiment_indices.get('cri', 0) / 10
# VF调整:GRI激发需求波动
# A/B低波动商品敏感性高,C类商品敏感性低
if commodity_grade in ['AX', 'AY', 'BX']: # 低波动等级
vf += gri * 0.20 # GRI最多增加+0.20
elif commodity_grade in ['AZ', 'BY', 'BZ']: # 中波动等级
vf += gri * 0.15 # GRI最多增加+0.15
else: # C类低价值商品
vf += gri * 0.08 # GRI最多增加+0.08
# LTF调整:LDI/CRI激发交期不确定性
# 所有等级都会受影响,但程度不同
ldi_impact = ldi * 0.15 # LDI影响最大
cri_impact = cri * 0.10 # CRI影响次要
ltf += ldi_impact + cri_impact # LTF最多增加+0.25
# XYZ维度对舆情的进一步放大(Z等级对舆情最敏感)
if commodity_grade.endswith('Z'): # Z等级:高波动
vf *= 1.2 # VF额外增加20%
ltf *= 1.2 # LTF额外增加20%
elif commodity_grade.endswith('Y'):
vf *= 1.1 # VF额外增加10%
ltf *= 1.1 # LTF额外增加10%
# X等级不额外放大
# 步骤3: 参数范围约束
vf = min(vf, 1.0) # VF最多1.0
ltf = min(ltf, 1.0) # LTF最多1.0
# 步骤4: 计算缓冲区参数
red_base = adu * dlt
red_safety = red_base * vf
red_zone = red_base + red_safety
yellow_zone = red_zone
green_zone = red_base * ltf * 0.5 # 绿区受LTF影响
min_stock = red_zone
max_stock = red_zone + yellow_zone + green_zone
reorder_point = red_zone + yellow_zone
# 步骤5: 获取补货模式描述
replenishment_mode = {
'AX': 'Push-MRP / 周期推式,固定安全库存',
'AY': 'Push-MRP + DDMRP / 动态缓冲,平衡库存',
'AZ': 'ATO / 订单驱动,最小库存',
'BX': 'MRP控制 / 定时盘点,平衡库存',
'BY': 'Croston + DDMRP / 间断需求预测,动态缓冲',
'BZ': 'DDMRP三层缓冲 / 强化控制,防止缺货',
'CX': '自动补充 / 最小库存,成本最优',
'CY': '低控制 / 简化管理,清货优先',
'CZ': 'MTO / 按单采购,零库存目标'
}
return {
'commodity_grade': commodity_grade,
'adu': round(adu, 2),
'dlt': dlt,
'base_vf': round(vf_matrix.get(commodity_grade, 0.25), 3),
'base_ltf': round(ltf_matrix.get(commodity_grade, 0.30), 3),
'adjusted_vf': round(vf, 3),
'adjusted_ltf': round(ltf, 3),
'buffer_zones': {
'red_base': round(red_base, 2),
'red_safety': round(red_safety, 2),
'red_total': round(red_zone, 2),
'yellow': round(yellow_zone, 2),
'green': round(green_zone, 2)
},
'inventory_levels': {
'min_stock': round(min_stock, 2),
'max_stock': round(max_stock, 2),
'reorder_point': round(reorder_point, 2)
},
'replenishment_mode': replenishment_mode.get(commodity_grade, '未知'),
'buffer_summary': {
'total_buffer': round(red_zone + yellow_zone + green_zone, 2),
'sentiment_adjusted': sentiment_indices is not None
}
}
实时库存触发补货机制(保持不变)
系统应根据净流位置(Net Flow Position)实时触发补货:
def trigger_replenishment_abc_xyz(
net_flow_position: float, # 当前净流位置
buffer_params: dict, # DDMRP缓冲区参数
commodity_grade: str # ABC-XYZ等级
) -> dict:
"""
ABC-XYZ等级适配的实时补货触发
不同等级的补货优先级策略不同
"""
reorder_point = buffer_params['reorder_point']
max_stock = buffer_params['max_stock']
min_stock = buffer_params['min_stock']
trigger_action = 'NO_ACTION'
replenishment_qty = 0
priority = 'NORMAL'
# ABC-XYZ等级对应的优先级映射
priority_map = {
'AX': {'CRITICAL': 'CRITICAL', 'HIGH': 'HIGH', 'NORMAL': 'NORMAL'},
'AY': {'CRITICAL': 'CRITICAL', 'HIGH': 'HIGH', 'NORMAL': 'MEDIUM'},
'AZ': {'CRITICAL': 'HIGH', 'HIGH': 'HIGH', 'NORMAL': 'MEDIUM'}, # 高价值波动商品优先级稍低
'BX': {'CRITICAL': 'HIGH', 'HIGH': 'MEDIUM', 'NORMAL': 'NORMAL'},
'BY': {'CRITICAL': 'HIGH', 'HIGH': 'MEDIUM', 'NORMAL': 'NORMAL'},
'BZ': {'CRITICAL': 'HIGH', 'HIGH': 'MEDIUM', 'NORMAL': 'LOW'}, # B类波动商品缺货控制重要
'CX': {'CRITICAL': 'MEDIUM', 'HIGH': 'LOW', 'NORMAL': 'LOW'},
'CY': {'CRITICAL': 'LOW', 'HIGH': 'LOW', 'NORMAL': 'LOW'},
'CZ': {'CRITICAL': 'LOW', 'HIGH': 'LOW', 'NORMAL': 'LOW'} # C类商品优先级最低
}
if net_flow_position < min_stock:
trigger_action = 'URGENT_REPLENISH'
replenishment_qty = max_stock - net_flow_position
priority = priority_map.get(commodity_grade, {}).get('CRITICAL', 'CRITICAL')
elif net_flow_position < reorder_point:
trigger_action = 'PLANNED_REPLENISH'
replenishment_qty = max_stock - net_flow_position
priority = priority_map.get(commodity_grade, {}).get('HIGH', 'HIGH')
elif net_flow_position < max_stock:
trigger_action = 'MONITOR'
replenishment_qty = max_stock - net_flow_position
priority = priority_map.get(commodity_grade, {}).get('NORMAL', 'NORMAL')
else:
trigger_action = 'NO_ACTION'
replenishment_qty = 0
priority = 'LOW'
return {
'commodity_grade': commodity_grade,
'net_flow_position': round(net_flow_position, 2),
'trigger_action': trigger_action,
'replenishment_qty': round(replenishment_qty, 2),
'priority': priority,
'zone_status': {
'red_zone_threshold': buffer_params['red_zone']['total'],
'yellow_zone_threshold': buffer_params['reorder_point'],
'current_position_zone': _determine_zone(net_flow_position, buffer_params)
}
}
def _determine_zone(position: float, params: dict) -> str:
"""确定当前库存位于哪个区域"""
min_stock = params['min_stock']
reorder_point = params['reorder_point']
if position < min_stock:
return 'RED_ZONE (Critical)'
elif position < reorder_point:
return 'YELLOW_ZONE (Warning)'
else:
return 'GREEN_ZONE (Safe)'
B等商品应用案例
背景:某生产企业生产长尾商品,月需求也是1-2次/月,每次销量也非常低。传统的MRP计算虽然看起来合理,但实际补货严重不足,反而会残留大量库存。
DDMRP实施策略:
案例1:间断需求商品(B类)的DDMRP应用
# 案例场景:特殊规格钢板,月需求1-2次
from datetime import datetime
import pandas as pd
import numpy as np
def ddmrp_replenishment_workflow(
sku: str,
current_net_flow: float,
historical_demand: list,
sentiment_indices: dict,
supplier_config: dict
):
"""
完整的DDMRP补货工作流程
"""
# 步骤1: 计算平均日用量(ADU)
adu = np.mean(historical_demand)
demand_std = np.std(historical_demand)
# 步骤2: 确定解耦提前期(DLT)
dlt = supplier_config['lead_time_days']
# 步骤3: 确定可变性系数(VF)和提前期系数(LTF)
# B类商品:基础VF=0.5, 基础LTF=0.5
base_vf = 0.5 if demand_std / adu > 0.3 else 0.3
base_ltf = 0.5
# 步骤4: 计算DDMRP缓冲区参数(含舆情调整)
buffer_params = calculate_ddmrp_buffer_parameters(
adu=adu,
dlt=dlt,
base_vf=base_vf,
base_ltf=base_ltf,
sentiment_indices=sentiment_indices,
commodity_grade='B' # B类商品
)
# 步骤5: 检查当前库存位置并触发补货
replenishment_decision = trigger_replenishment(
net_flow_position=current_net_flow,
buffer_params=buffer_params
)
# 步骤6: 生成补货建议
recommendation = {
'sku': sku,
'timestamp': datetime.now().isoformat(),
'current_position': current_net_flow,
'buffer_configuration': buffer_params,
'replenishment_decision': replenishment_decision,
'supplier_recommendation': {
'supplier': supplier_config['name'],
'suggested_order_qty': replenishment_decision['replenishment_qty'],
'order_cost': replenishment_decision['replenishment_qty'] * supplier_config['unit_price'],
'lead_time': supplier_config['lead_time_days'],
'urgency': replenishment_decision['priority']
},
'expected_outcomes': {
'inventory_reduction': f"{(buffer_params['min_stock'] - buffer_params['red_zone']['total']) / buffer_params['min_stock'] * 100:.1f}%",
'service_level_maintained': '>=95%',
'replenishment_frequency': f"每月{30 / dlt:.0f}次左右"
}
}
return recommendation
# 实际应用示例
if __name__ == '__main__':
# B类商品:特殊规格钢板
historical_demand = [
0, 0, 50, 0, 0, 0, 60, 0, 0, 45,
0, 0, 0, 55, 0, 0, 0, 0, 58, 0
] # 20天的销售数据,存在大量零点
recommendation = ddmrp_replenishment_workflow(
sku='STEEL_PLATE_SPECIAL_001',
current_net_flow=150, # 当前库存水平
historical_demand=historical_demand,
sentiment_indices={
'ldi': 4, # 物流延迟中等
'cri': 5, # 合规风险中等
'gri': 2, # 地缘政治风险低
'fri': 3 # 金融风险低
},
supplier_config={
'name': '宝山供应商B',
'unit_price': 3100,
'lead_time_days': 7,
'capacity': 500,
'moq': 50
}
)
print("\n=== DDMRP补货建议 ===")
print(f"SKU: {recommendation['sku']}")
print(f"当前库存位置: {recommendation['current_position']}")
print(f"\n缓冲区配置:")
print(f" 红区总计: {recommendation['buffer_configuration']['red_zone']['total']}")
print(f" 黄区: {recommendation['buffer_configuration']['yellow_zone']}")
print(f" 绿区: {recommendation['buffer_configuration']['green_zone']}")
print(f" 最小库存值: {recommendation['buffer_configuration']['min_stock']}")
print(f" 最大库存值: {recommendation['buffer_configuration']['max_stock']}")
print(f"\n补货决策:")
print(f" 触发动作: {recommendation['replenishment_decision']['trigger_action']}")
print(f" 建议订购量: {recommendation['replenishment_decision']['replenishment_qty']} 单位")
print(f" 优先级: {recommendation['replenishment_decision']['priority']}")
print(f" 当前区域: {recommendation['replenishment_decision']['zone_status']['current_position_zone']}")
print(f"\n供应商建议:")
print(f" 供应商: {recommendation['supplier_recommendation']['supplier']}")
print(f" 订单总成本: ¥{recommendation['supplier_recommendation']['order_cost']:.2f}")
print(f" 预计效果:")
print(f" 库存水平降低: {recommendation['expected_outcomes']['inventory_reduction']}")
print(f" 服务水平: {recommendation['expected_outcomes']['service_level_maintained']}")
print(f" 补货频率: {recommendation['expected_outcomes']['replenishment_frequency']}")
关键要点:
- 动态缓冲区 - 利用VF和LTF的舆情调整,使缓冲区能够主动应对风险
- 实时触发 - 基于净流位置的触发机制替代定期补货,反应更敏捷
- 三层分层 - 红区(紧急)、黄区(计划)、绿区(监控),清晰的决策规则
- B类商品优化 - 特别适合间断需求,相比Croston法更稳健
- 舆情融合 - 通过LDI/CRI/GRI/FRI指标动态调整参数,主动防范风险
PuLP求解器实现
完整的优化引擎
from pulp import *
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
class IntelligentReplenishmentEngine:
"""
智能补货决策引擎
- 整合舆情指标优化安全库存
- 多供应商模式应对断裂风险
- 考虑资金成本与缺货成本的权衡
"""
def __init__(self, commodity_code: str, buyer_geo_key: str):
self.commodity_code = commodity_code # 商品代码
self.buyer_geo_key = buyer_geo_key # 采购商地理位置
self.problem = None
self.decision_vars = {}
def calculate_safety_stock(self,
avg_demand: float,
demand_std: float,
lead_time_days: int,
service_level: float = 0.95,
sentiment_indices: dict = None) -> float:
"""
计算风险调整后的安全库存
基础安全库存公式 (EOQ变体):
SS = Z_α × σ_demand × sqrt(L)
舆情调整:
- LDI增高 → lead_time增加 → SS↑
- GRI增高 → 需求波动增加 → SS↑
- CRI增高 → 供应不确定↑ → SS↑
"""
# 服务水平对应的标准差倍数
z_scores = {0.90: 1.28, 0.95: 1.645, 0.99: 2.33}
z = z_scores.get(service_level, 1.645)
# 基础安全库存
base_ss = z * demand_std * np.sqrt(lead_time_days)
# 舆情调整因子
adjustment_factor = 1.0
if sentiment_indices:
# LDI增加的配送时间
if 'ldi' in sentiment_indices:
ldi_adjusted_lead_time = lead_time_days * (1 + sentiment_indices['ldi'] / 10 * 0.3)
base_ss = z * demand_std * np.sqrt(ldi_adjusted_lead_time)
# GRI增加的需求不确定性
if 'gri' in sentiment_indices:
gri_factor = sentiment_indices['gri'] / 10 # 0-1
demand_std_adjusted = demand_std * (1 + gri_factor * 0.4)
base_ss = z * demand_std_adjusted * np.sqrt(lead_time_days)
# CRI增加的供应风险
if 'cri' in sentiment_indices:
cri_factor = sentiment_indices['cri'] / 10
adjustment_factor *= (1 + cri_factor * 0.2)
safety_stock = base_ss * adjustment_factor
return safety_stock
def optimize_replenishment(self,
current_inventory: float,
forecast_demand: np.ndarray, # 未来30天的日需求
holding_cost_per_unit: float, # 库存成本 (¥/单位/天)
stockout_cost_per_unit: float, # 缺货成本 (¥/单位)
suppliers: list, # 供应商列表
sentiment_indices: dict = None) -> dict:
"""
使用整数线性规划求解最优补货方案
参数示例:
current_inventory: 100 (吨)
forecast_demand: [50, 55, 45, ..., 60] (30天)
holding_cost: 50 (¥/吨/天)
stockout_cost: 2000 (¥/吨)
suppliers: [
{
'name': '供应商A',
'unit_price': 3000, # ¥/吨
'lead_time': 5, # 天
'capacity': 500, # 吨/月
'moq': 50, # 最小订购量
'quality_score': 0.95,
'reliability': 0.9
},
...
]
"""
days = len(forecast_demand)
n_suppliers = len(suppliers)
# 创建LP问题
prob = LpProblem("Replenishment_Optimization", LpMinimize)
# 决策变量
# x[i][j]: 从供应商j在第i天的订购量 (吨)
x = [[LpVariable(f"order_day{i}_supplier{j}", lowBound=0, cat='Continuous')
for j in range(n_suppliers)] for i in range(days)]
# h[i]: 第i天末的库存量 (吨)
h = [LpVariable(f"inventory_day{i}", lowBound=0, cat='Continuous') for i in range(days)]
# s[i]: 第i天的缺货量 (吨)
s = [LpVariable(f"shortage_day{i}", lowBound=0, cat='Continuous') for i in range(days)]
# y[i][j]: 是否从供应商j在第i天订购 (二元变量, 用于MOQ约束)
y = [[LpVariable(f"order_flag_day{i}_supplier{j}", cat='Binary')
for j in range(n_suppliers)] for i in range(days)]
# ===== 目标函数 =====
# 最小化: 库存持有成本 + 缺货损失 + 订货次数溢价 (多源溢价)
cost_holding = lpSum([h[i] * holding_cost_per_unit for i in range(days)])
cost_shortage = lpSum([s[i] * stockout_cost_per_unit for i in range(days)])
# 多源溢价 (从>1个供应商订购时增加成本5%)
multi_source_premium = lpSum([
y[i][j] * suppliers[j]['unit_price'] * 0.05 * lpSum([x[i][j_] for j_ in range(n_suppliers)])
for i in range(days) for j in range(n_suppliers)
])
prob += cost_holding + cost_shortage + multi_source_premium, "Total_Cost"
# ===== 约束条件 =====
# 1. 库存平衡约束
for i in range(days):
if i == 0:
# 第一天: 当前库存 + 订单 - 需求 = 库存 + 缺货
prob += (
current_inventory +
lpSum([x[i][j] for j in range(n_suppliers)]) -
forecast_demand[i] == h[i] + s[i],
f"Balance_Day{i}"
)
else:
# 后续天: 前日库存 + 订单 - 需求 = 库存 + 缺货
prob += (
h[i-1] +
lpSum([x[i][j] for j in range(n_suppliers)]) -
forecast_demand[i] == h[i] + s[i],
f"Balance_Day{i}"
)
# 2. 最小订购量 (MOQ) 约束
for i in range(days):
for j in range(n_suppliers):
moq = suppliers[j]['moq']
# 如果订购,则订购量 >= MOQ
prob += (
x[i][j] >= moq * y[i][j],
f"MOQ_Day{i}_Supplier{j}"
)
# 最大订购量 (供应商容量)
prob += (
x[i][j] <= suppliers[j]['capacity'] * y[i][j],
f"Capacity_Day{i}_Supplier{j}"
)
# 3. 安全库存约束 (风险调整)
avg_demand = np.mean(forecast_demand)
demand_std = np.std(forecast_demand)
# 计算风险调整的安全库存
safety_stock = self.calculate_safety_stock(
avg_demand, demand_std,
lead_time_days=suppliers[0]['lead_time'], # 使用最快的供应商
sentiment_indices=sentiment_indices
)
# 平均库存 >= 安全库存
avg_inventory = lpSum([h[i] for i in range(days)]) / days
prob += avg_inventory >= safety_stock, "Safety_Stock_Constraint"
# 4. 供应商可靠性约束 (考虑舆情风险)
for i in range(days):
for j in range(n_suppliers):
# 高风险环境下,不完全依赖单一供应商
if sentiment_indices and sentiment_indices.get('cri', 0) > 6:
# CRI>6时,单个供应商订购量不超过预测需求的60%
prob += (
x[i][j] <= forecast_demand[i] * 0.6,
f"Multi_Source_Diversity_Day{i}_Supplier{j}"
)
# 5. 缺货容忍度约束 (特定采购商)
# 采购商等级A: 不允许缺货 (s[i]=0)
# 采购商等级B: 缺货<平均需求的5%
# 采购商等级C: 缺货<平均需求的10%
buyer_grades = {'A': 0.0, 'B': 0.05, 'C': 0.10}
buyer_grade = 'B' # 默认B级
max_shortage_ratio = buyer_grades.get(buyer_grade, 0.05)
prob += (
lpSum([s[i] for i in range(days)]) <= avg_demand * max_shortage_ratio * days,
"Shortage_Tolerance"
)
# ===== 求解 =====
prob.solve(PULP_CBC_CMD(msg=0))
# ===== 提取结果 =====
replenishment_plan = []
for i in range(days):
day_orders = []
for j in range(n_suppliers):
order_qty = x[i][j].varValue
if order_qty and order_qty > 0.1: # 订购量>0.1单位
day_orders.append({
'supplier': suppliers[j]['name'],
'quantity': round(order_qty, 2),
'unit_price': suppliers[j]['unit_price'],
'total_cost': round(order_qty * suppliers[j]['unit_price'], 2),
'lead_time': suppliers[j]['lead_time']
})
if day_orders: # 只记录有订单的天
replenishment_plan.append({
'day': i + 1,
'orders': day_orders,
'inventory_end_of_day': round(h[i].varValue, 2),
'shortage': round(s[i].varValue, 2)
})
# 总成本分析
total_cost = value(prob.objective)
holding_cost_actual = sum([h[i].varValue * holding_cost_per_unit for i in range(days)])
shortage_cost_actual = sum([s[i].varValue * stockout_cost_per_unit for i in range(days)])
return {
'status': LpStatus[prob.status],
'optimization_successful': LpStatus[prob.status] == 'Optimal',
'total_cost': round(total_cost, 2),
'cost_breakdown': {
'holding_cost': round(holding_cost_actual, 2),
'shortage_cost': round(shortage_cost_actual, 2),
'multi_source_premium': round(total_cost - holding_cost_actual - shortage_cost_actual, 2)
},
'replenishment_plan': replenishment_plan,
'summary': {
'total_orders_days': len(replenishment_plan),
'avg_inventory_level': round(np.mean([h[i].varValue for i in range(days)]), 2),
'total_shortage': round(sum([s[i].varValue for i in range(days)]), 2),
'safety_stock_calculated': round(safety_stock, 2)
}
}
使用示例
if __name__ == '__main__':
engine = IntelligentReplenishmentEngine(
commodity_code='STEEL_REBAR',
buyer_geo_key='CHINA_EAST_001'
)
# 供应商配置
suppliers = [
{
'name': '鞍山供应商A',
'unit_price': 3000,
'lead_time': 3,
'capacity': 500,
'moq': 50,
'quality_score': 0.95,
'reliability': 0.92
},
{
'name': '宝山供应商B',
'unit_price': 3100,
'lead_time': 5,
'capacity': 400,
'moq': 40,
'quality_score': 0.90,
'reliability': 0.85
},
{
'name': '日本进口供应商C',
'unit_price': 3500,
'lead_time': 20,
'capacity': 200,
'moq': 100,
'quality_score': 0.98,
'reliability': 0.95
}
]
# 舆情数据
sentiment = {
'ldi': 5, # 物流延迟中等
'cri': 6, # 合规风险高
'gri': 3, # 地缘政治风险低
'fri': 4 # 金融风险中等
}
# 预测需求 (30天)
forecast = np.array([
50, 55, 45, 60, 52, 48, 65, 70, 55, 50,
58, 62, 51, 49, 67, 72, 58, 53, 61, 68,
54, 50, 59, 64, 52, 47, 63, 69, 55, 51
])
# 求解
result = engine.optimize_replenishment(
current_inventory=100,
forecast_demand=forecast,
holding_cost_per_unit=50,
stockout_cost_per_unit=2000,
suppliers=suppliers,
sentiment_indices=sentiment
)
print("优化结果:")
print(f" 优化状态: {result['status']}")
print(f" 总成本: ¥{result['total_cost']}")
print(f" 其中: 库存成本 ¥{result['cost_breakdown']['holding_cost']}, " +
f"缺货成本 ¥{result['cost_breakdown']['shortage_cost']}")
print(f" 采购计划: 共{result['summary']['total_orders_days']}天有订单")
后续扩展方向
- 供应链可视化 - 实时库存热力图、区域预警仪表板
- 成本优化 - 仓储费用、缺货机会成本、滞销贾值
- 多层级BOM - 扩展到生产型企业的多层物料规划
- 外部数据融合 - 天气、事件预测增强舆情预测准确度
- AI自适应 - 利用历史数据自动学习最优的VF/LTF参数