Python 使用np.polyfit外推曲线,得到意外结果

Python 使用np.polyfit外推曲线,得到意外结果,python,pandas,numpy,Python,Pandas,Numpy,我正试图“拟合曲线”一些期权波动率数据和它们的增量,我正努力确定哪种模型最适合拟合这条曲线并进行外推,以便预测尚未列出的行权的波动率和增量 我在这里读了很多不同的答案,到目前为止没有一个对我有帮助,但是如果有一个我错过了,我会提前道歉 假设我的df如下所示,以执行价为指数,MidVol为买入价和卖出价之间的中间价,然后是每次执行的各自差值 MidVol CallDelta PutDelta 4000.0 0.757832 0.910918 -0.089082 500

我正试图“拟合曲线”一些期权波动率数据和它们的增量,我正努力确定哪种模型最适合拟合这条曲线并进行外推,以便预测尚未列出的行权的波动率和增量

我在这里读了很多不同的答案,到目前为止没有一个对我有帮助,但是如果有一个我错过了,我会提前道歉

假设我的
df
如下所示,以执行价为指数,
MidVol
为买入价和卖出价之间的中间价,然后是每次执行的各自差值

         MidVol    CallDelta PutDelta
4000.0   0.757832  0.910918 -0.089082
5000.0   0.739650  0.844523 -0.155477
6000.0   0.742915  0.766228 -0.233772
7000.0   0.733530  0.685637 -0.314363
8000.0   0.753219  0.610900 -0.389100
9000.0   0.750366  0.539006 -0.460994
10000.0  0.756793  0.476428 -0.523572
11000.0  0.774761  0.426470 -0.573530
12000.0  0.781004  0.379058 -0.620942
14000.0  0.795634  0.303317 -0.696683
16000.0  0.812305  0.247911 -0.752089
18000.0  0.831367  0.207874 -0.792126
20000.0  0.852848  0.179159 -0.820841
我首先要做的是,为
MidVol
列计算一行最佳拟合,然后使用该曲线进行外推,并为不存在的罢工获取可能的
MidVol
值。例如,30000.0罢工的
MidVol
是什么

我现在的做法是:

curve = np.poly1d(np.polyfit(df.index, df['MidVol'], deg=5))
这就产生了这条曲线,我不得不说我很满意,尽管我可能会把
度减少到4度,因为我觉得它有点过拟合

但是,我的下一个问题是,尝试计算30000.0版本上的
MidVol

目前,如果我使用此代码计算30000.0击数(30000)
曲线,我会得到
0.506
的结果,尽管每次运行代码时都会发生变化。这显然是不正确的,我希望这一地区的结果可能是
0.95
。有人能告诉我我做错了什么吗

非常感谢

编辑 多亏了Chris的回答,我现在认为
interp1d(df.index,df['MidVol'],kind='quadratic',fill_value='extraction')
是一条可行之路,但是,我无法复制Chris在其示例中所做的平滑曲线。我在数据点之间得到了一条参差不齐的线性线,尽管在外推时得到了我期望的值

我尝试了不同类型的
kind=
,它们都产生了相同的图表,除了外推的数字

我使用的完整代码如下:

import pandas as pd
import matplotlib.pyplot as plt
from scipy import interpolate

df = pd.DataFrame({'Strike':[4000,5000,6000,7000,8000,9000,10000,11000,12000,14000,16000,18000,20000],
                   'Vol': [0.757,0.739,0.742,0.733,0.753,0.750,0.756,0.774,0.781,0.795,0.812,0.831,0.852]})

norm_fit = interpolate.interp1d(df['Strike'], df['Vol'], fill_value='extrapolate')
cubic_fit = interpolate.interp1d(df['Strike'], df['Vol'], fill_value='extrapolate', kind='cubic')
quad_fit = interpolate.interp1d(df['Strike'], df['Vol'], fill_value='extrapolate', kind='quadratic')

norm = [norm_fit(x) for x in df['Strike']]
cubic = [cubic_fit(x) for x in df['Strike']]
quad = [quad_fit(x) for x in df['Strike']]

new_strikes = [22000, 24000, 26000, 28000]
new_norm = [norm_fit(x) for x in new_strikes]
new_cub = [cubic_fit(x) for x in new_strikes]
new_quad = [quad_fit(x) for x in new_strikes]

plt.plot(df['Strike'], norm, color='orange')
plt.plot(new_strikes, new_norm, color='orange')
plt.plot(df['Strike'], cubic, color='r')
plt.plot(new_strikes, new_cub, color='r')
plt.plot(df['Strike'], quad, color='b')
plt.plot(new_strikes, new_quad, color='b')
plt.show()

为什么我得不到平滑曲线?

我会尝试使用
scipy

from scipy.interpolate import interp1d
# interpolate data of x and y using a quadratic regression and extrapolate
f = interp1d(df.index, df['MidVol'], kind='quadratic', fill_value='extrapolate')
f(30000)
# array(0.99582528)
下面是一个快速绘图:

import matplotlib.pyplot as plt

sample = [20000, 22000,24000,26000,28000,30000]
extrap = [f(x).item() for x in sample]

plt.scatter(df.index, df['MidVol'])
plt.plot(sample, extrap, color='r')
plt.plot(df.index, curve(df.index), color='r')
plt.show()

更新

拟合对于您拥有的点来说似乎非常好,但当然这并不意味着曲线对于超出该范围的值来说一定是一个“好”的预测值。试着在图的两端添加一些值,你可能会看到拟合的多项式到处都是。嗨,伙计,这正是我的问题,这是一个很好的拟合,但我没有额外的数据,因为罢工尚未列出,因此需要某种外推或插值。简单的多项式曲线拟合不太可能成功解决拟合范围外的外推问题。一个五次多项式有四个零梯度点——它必须在拟合范围外快速旋转。嗨,伙计,对不起,实际上可能是跳枪了。我想你的答案会解决我的问题,但我无法让它发挥作用。使用interp1d代码时,f(30000)的值为0.84?罢工次数越高,中位数继续下降,f(40000)为0.63。你知道这是什么原因吗?@topbantz你的实际数据或样本数据得到了.84?嗨,伙计,我发现这有一些非常奇怪的行为。当我早些时候写信给你时,我在做f(30000)时得到了0.84,在做f(40000)时得到了0.63。我现在在另一个文件中尝试了完全相同的代码,现在得到了正确的外推值,但不再得到平滑曲线了?我已经更新了我的问题,以显示我的经历。许多的thanks@topbantz对于已知的
Strike
无法获得平滑曲线的原因是,您正在“外推”已知值的数据:
quad=[quad_fit(x)for x in df['Strike']]
当您对已知的Strike进行
quad_fit()
时,它将给出确切的数值。这有意义吗?嗨,克里斯,非常感谢你的回答,非常透彻。是的,我认为这是有道理的。非常感谢你的帮助
df = pd.DataFrame({'Strike':[4000,5000,6000,7000,8000,9000,10000,11000,12000,14000,16000,18000,20000],
                   'Vol': [0.757,0.739,0.742,0.733,0.753,0.750,0.756,0.774,0.781,0.795,0.812,0.831,0.852]})

norm_fit = interpolate.interp1d(df['Strike'], df['Vol'], fill_value='extrapolate', kind='linear')
cubic_fit = interpolate.interp1d(df['Strike'], df['Vol'], fill_value='extrapolate', kind='cubic')
quad_fit = interpolate.interp1d(df['Strike'], df['Vol'], fill_value='extrapolate', kind='quadratic')

# norm = [norm_fit(x) for x in df['Strike']]
# cubic = [cubic_fit(x) for x in df['Strike']]
# quad = [quad_fit(x) for x in df['Strike']]

# new code
quad_curve = np.poly1d(np.polyfit(df['Strike'], df['Vol'], deg=4))
cubic_curve = np.poly1d(np.polyfit(df['Strike'], df['Vol'], deg=3))
linear_curve = np.poly1d(np.polyfit(df['Strike'], df['Vol'], deg=1))

new_strikes = [22000, 24000, 26000, 28000]
new_norm = [norm_fit(x) for x in new_strikes]
new_cub = [cubic_fit(x) for x in new_strikes]
new_quad = [quad_fit(x) for x in new_strikes]

# plt.plot(df['Strike'], norm, color='orange')
plt.plot(df['Strike'], linear_curve(df['Strike']), color='orange')
plt.plot(new_strikes, new_norm, color='orange')

# plt.plot(df['Strike'], cubic, color='r')
plt.plot(df['Strike'], cubic_curve(df['Strike']), color='r')
plt.plot(new_strikes, new_cub, color='r')

# plt.plot(df['Strike'], quad, color='b')
plt.plot(df['Strike'], quad_curve(df['Strike']), color='b')
plt.plot(new_strikes, new_quad, color='b')

plt.scatter(df['Strike'], df['Vol'], color='g')

plt.show()