我试图在python上使用牛顿算法获得函数的根。即使我更改了精度级别,我也有运行时错误。能帮助我了解我该如何改进它?
最佳, GB
在我的“简单”代码和根查找部分下面:
from scipy.stats import norm
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize as opt
def vega_callspread(r,S,T,d,sigma,q,K1,K2):
d1 = (np.log(S/K1)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
d2 = (np.log(S/K2)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
u1 = S*np.exp(-q*T)*norm.pdf(d1,0,1)
u2 = S*np.exp(-q*T)*norm.pdf(d2,0,1)
return u1-u2;
x0=112
r=0
T=1
d=0
sigma=0.2
q=0
K1=110
K2=130
res2= opt.newton(vega_callspread, x0, args=(r,T,d,sigma,q,K1,K2,),tol=10**(-1),maxiter=1000000)
the error i get is: res2= opt.zeros.newton(vega_callspread, x0, args=(r,T,d,sigma,q,K1,K2,),tol=10**(-1),maxiter=1000000)
/Users/anaconda/lib/python3.5/site-packages/scipy/optimize/zeros.py:173: RuntimeWarning: Tolerance of 0.011300000000005639 reached
warnings.warn(msg, RuntimeWarning)
答案 0 :(得分:1)
很难用如此稀疏的语境提供建议。但有些评论:
您没有使用牛顿,如here所述:
如果提供了func的导数fprime,则使用Newton-Raphson方法,否则使用割线方法。
您的错误来自here我会说:
由于这些tol值是硬编码的,我想这不应该发生!
# Secant method
p0 = x0
if x0 >= 0:
p1 = x0*(1 + 1e-4) + 1e-4
else:
p1 = x0*(1 + 1e-4) - 1e-4
q0 = func(*((p0,) + args))
q1 = func(*((p1,) + args))
for iter in range(maxiter):
if q1 == q0:
if p1 != p0:
msg = "Tolerance of %s reached" % (p1 - p0)
warnings.warn(msg, RuntimeWarning)
return (p1 + p0)/2.0
含义:你的代码可能有问题!
让我们尝试更慢但更安全的bisection-method:
# brackets not tuned! it's just some trial!
res2 = opt.bisect(vega_callspread, 0, 200, args=(r,T,d,sigma,q,K1,K2))
输出:
X:\so_newton.py:9: RuntimeWarning: divide by zero encountered in log
d1 = (np.log(S/K1)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
X:\so_newton.py:10: RuntimeWarning: divide by zero encountered in log
d2 = (np.log(S/K2)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
这是一个不好的迹象。
你的功能是:
def vega_callspread(r,S,T,d,sigma,q,K1,K2):
d1 = (np.log(S/K1)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
d2 = (np.log(S/K2)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
u1 = S*np.exp(-q*T)*norm.pdf(d1,0,1)
u2 = S*np.exp(-q*T)*norm.pdf(d2,0,1)
return u1-u2;
你打电话给:
x0=112
r=0
T=1
d=0
sigma=0.2
q=0
K1=110
K2=130
args=(r,T,d,sigma,q,K1,K2,)
没有S!
所以你要么危险地重命名变量,要么你的结尾有一些错误!
S
的值是log(S/K1)
和co。
它不是关于S/K1
而是关于此:
import numpy as np
np.log(0)
# __main__:1: RuntimeWarning: divide by zero encountered in log
# -inf
我是从this SO-answer获得的。
您现在可以尝试解释这对您的函数的作用,因为此log-eval将获得-np.inf
的值。
我懒得阅读args-handling的文档/来源,但让我们检查一下(给func添加一个打印件;使用之前的二分法):
def vega_callspread(r,S,T,d,sigma,q,K1,K2):
print('S: ', S)
d1 = (np.log(S/K1)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
d2 = (np.log(S/K2)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
u1 = S*np.exp(-q*T)*norm.pdf(d1,0,1)
u2 = S*np.exp(-q*T)*norm.pdf(d2,0,1)
return u1-u2;
输出:
S: 0
X:\so_newton.py:11: RuntimeWarning: divide by zero encountered in log
d1 = (np.log(S/K1)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
X:\so_newton.py:12: RuntimeWarning: divide by zero encountered in log
d2 = (np.log(S/K2)+(r+sigma*sigma/2)*T)/(sigma*np.sqrt(T))
S: 0
所以似乎arg-handling是通过arg-name(而不仅仅是调用中的param-position;我在这里错过了正确的编程语言术语;再次懒惰!)
这个(S = 0)可能非常糟糕(而不是你想要的),这是你的错误!
修改强>
发表评论后,您似乎尝试优化S
。这让我很清楚,您正在使用一些优化算法优化x
,而您的函数中没有x
!
我没有在这里分析您的任务,但您可能希望让您的功能使用一些x
(由x0
初始化),因为这是{{1}的一般概念}。我们可以保留名称scipy.optimize
,但它必须是函数的第一个参数。这些都在文档中解释。
所以:
S
输出:
def vega_callspread(S, r,T,d,sigma,q,K1,K2): # S now first argument !!!
...
res2= opt.newton(vega_callspread, x0, args=(r,T,d,sigma,q,K1,K2,),tol=10**(-1),maxiter=1000000) # S removed from args; S is init by x0 -> read docs!