You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中替代Excel Solver调整两个变量最小化误差项

需求说明

我正在将旧的VBA代码转换为Python脚本,核心需求是通过调整asset_val(资产价值)和asset_vol(资产波动率)两个列的取值,最小化误差项。


原VBA核心代码

原VBA调用Excel的Solver功能逐行完成优化,其中第28列为误差值,第20、21列分别为初始资产价值、初始资产波动率:

For k = startobs To (endobs - 1) Step 1

SolverReset
SolverOK SetCell:=Worksheets("Select").Cells(k, 28).address, MaxMinVal:=2, ByChange:=Range(Cells(k, 20), Cells(k, 21)).address
SolverReset
SolverOK SetCell:=Worksheets("Select").Cells(k, 28).address, MaxMinVal:=2, ByChange:=Range(Cells(k, 20), Cells(k, 21)).address
    
SolverOptions precision:=0.00001, Iterations:=300, Scaling:=True, Convergence:=0.00001

现有Python基础实现

误差项计算

误差项综合考虑期权定价偏差和波动率偏差,计算公式如下:

def Error(S, K, T, r, sigma, E, ewma_vol):
    return ((BnSCall(S, K, T, r, sigma) - E)**2 + (SigmaByEquity(S, K, T, r, sigma) - ewma_vol * E)**2)

EWMA(指数加权移动平均)波动率计算

def EstVolEWMA(log_ret, lamda): 
    est_vol_ewma = abs(log_ret*0)
    est_vol_ewma_prev = 0           
    for date in est_vol_ewma.index[50:]:
        est_vol_ewma_t = (lamda*(est_vol_ewma_prev**2)+(1-lamda)*(log_ret[date]**2)*52)**0.5 
        est_vol_ewma[date] = est_vol_ewma_t
        est_vol_ewma_prev = est_vol_ewma_t
    return est_vol_ewma

布莱克-斯科尔斯看涨期权定价

def BnSCall(S,K,T,r,sigma):
    return S*norm.cdf(BnSd1(S,K,T,r,sigma), 0, 1)-K*np.exp(-np.log(1+r)*T)*norm.cdf(BnSd2(S,K,T,r,sigma))

初始值计算

初始资产价值

def InitAssetVal(debt, avv_mcap): 
    return debt + avv_mcap
data['asset_val'] = InitAssetVal(data['debt interpolated'], data['av_m_cap'])

初始资产波动率

def InitAssetVol(equity_vol): 
    return equity_vol*0.5
data['asset_vol'] = InitAssetVol(data['est_vol_ewma'])

Python优化实现(替代VBA Solver功能)

使用scipy.optimize.minimize实现和VBA Solver参数完全对齐的逐行优化逻辑,代码如下:

import scipy.optimize as opt
import numpy as np

# 提前计算全量EWMA波动率,避免优化时重复计算
data['ewma_vol'] = EstVolEWMA(data['log_ret'], lamda=0.94)
# 替换为实际需要优化的观测范围
startobs = 50
endobs = len(data)
optimize_range = data.index[startobs: endobs-1]

for idx in optimize_range:
    # 提取当前行固定参数
    row = data.loc[idx]
    K = row['debt interpolated']
    T = 1
    r = row['12m rate'] / 100
    E = row['av_m_cap']
    ewma_vol = row['ewma_vol']
    
    # 构造当前行专属误差函数,入参为[资产价值, 资产波动率]
    def row_error(x):
        S = x[0]
        sigma = x[1]
        # 资产价值、波动率不能为负,异常值返回无穷大
        if S <= 0 or sigma <= 0:
            return np.inf
        return Error(S, K, T, r, sigma, E, ewma_vol)
    
    # 取预计算的初始值
    x0 = [row['asset_val'], row['asset_vol']]
    # 求解参数完全对齐原VBA Solver配置
    res = opt.minimize(row_error, x0, method='L-BFGS-B', 
                      bounds=((1e-6, None), (1e-6, None)),
                      tol=1e-5,
                      options={'maxiter': 300, 'disp': False})
    # 写入优化结果
    data.loc[idx, 'asset_val'] = res.x[0]
    data.loc[idx, 'asset_vol'] = res.x[1]
    data.loc[idx, 'Error'] = res.fun

注意事项

  • 需提前实现BnSd1、BnSd2、SigmaByEquity三个依赖函数,保证逻辑和原实现一致
  • 若优化收敛效果不佳,可将求解方法替换为Nelder-Mead适配非平滑误差场景
  • 批量优化速度较慢时,可通过joblib等并行库对逐行逻辑做并行加速

内容的提问来源于stack exchange,提问作者JamaJohnsen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 13:45:02