首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >优化期权定价码

优化期权定价码
EN

Code Review用户
提问于 2020-01-05 16:56:54
回答 1查看 86关注 0票数 1

我是Python新手,正在尝试优化以下代码:

代码语言:javascript
复制
import sys
import numpy as np

def LSMPut(T, r, sigma, K, S0, TimeSteps, Paths, k):
    dt = T/TimeSteps
    t = np.arange(0, T+dt, dt).tolist()

    z=[np.random.standard_normal() for _ in range(Paths)]
    w = (r-sigma**2/2)*T + sigma*np.sqrt(T)*np.array(z)

    S = S0*np.exp(np.array(w))
    P=np.maximum(K-np.array(S),0)

    for i in range(TimeSteps-1, -1, -1):
        z=[np.random.standard_normal() for _ in range(Paths)]
        w = t[i]*np.array(w)/t[i+1] + sigma*np.sqrt(dt*t[i]/t[i+1])*np.array(z)

        S = S0*np.exp(np.array(w))
        itmPaths = [index for index,value in enumerate(K-np.array(S)) if value > 0]

        itmS = S[itmPaths]
        Pt = K - np.array(itmS)

        itmDiscP = P[itmPaths]*np.exp(-r*dt)

        A = BasisFunct(itmS, k)
        beta = np.linalg.lstsq(A,itmDiscP)[0]
        C = np.dot(A,beta)

        exPaths = [itmPaths[i] for i, value in enumerate(zip(Pt, C)) if value[0] > value[1]]
        restPaths = np.setdiff1d(np.arange(0, Paths-1, 1).tolist(), exPaths) # Rest of the paths

        P[exPaths] = [Pt[i] for i, value in enumerate(zip(Pt, C)) if value[0] > value[1]]
        P[restPaths] = np.array(P[restPaths])*np.exp(-r*dt)

        u=np.mean(P*np.exp(-r*dt))

    return u

def BasisFunct(X, k):
    Ones=[1 for _ in range(len(X))]
    if k == 1:
        A = np.column_stack((Ones,1 - np.array(X)))
    elif k == 2:
        A = np.column_stack((Ones,1 - np.array(X),1/2*(2-4*np.array(X) + np.array(X)**2)))
    elif k == 3:
        A = np.column_stack((Ones,1 - np.array(X),1/2*(2-4*np.array(X) + np.array(X)**2), 1/6*(6-18*np.array(X) + 9*np.array(X)**2-np.array(X)**3)))
    elif k == 4:
        A = np.column_stack((Ones,1 - np.array(X),1/2*(2-4*np.array(X) + np.array(X)**2), 1/6*(6-18*np.array(X) + 9*np.array(X)**2-np.array(X)**3),1/24*(24 - 96*np.array(X) + 72*np.array(X)**2 - 16*np.array(X)**3 + np.array(X)**4)))
    elif k == 5:
        A = np.column_stack((Ones,1 - np.array(X),1/2*(2-4*np.array(X) + np.array(X)**2), 1/6*(6-18*np.array(X) + 9*np.array(X)**2-np.array(X)**3),1/24*(24 - 96*np.array(X) + 72*np.array(X)**2 - 16*np.array(X)**3 + np.array(X)**4),1/120*(120-600*np.array(X)+600*np.array(X)**2-200*np.array(X)**3+25*np.array(X)**4-np.array(X)**5)))
    else:
            sys.exit("Too many basis functions requested")
    return A

print(LSMPut(1, 0.06, 0.15, 100, 90, 20, 1000000, 5))

代码的目的是计算美国看跌期权的价格。如果Paths > 1,000,000需要很长时间来执行,特别是当我执行灵敏度分析时。我想知道是否有任何方法来优化代码或加快处理时间。谢谢。

EN

回答 1

Code Review用户

回答已采纳

发布于 2020-01-06 06:02:43

我将脚本的主要部分更改为:

代码语言:javascript
复制
from time import time
start = time()
val = LSMPut(1, 0.06, 0.15, 100, 90, 20, 40_000, 5)
print(f'Done in {time() - start:.3} seconds')
print(val)

重点是Python将打印计算运行所需的时间。修改前,脚本需要3.05秒才能在我的计算机上运行。

现在,我可以修改lsmput (我重新命名了LSMPut和其他一些函数和变量,因为它们的名称不应该包含大写字母,根据Python指南),并查看哪些更改使其运行得更快。

下面是对运行时影响最大的更改。改变

代码语言:javascript
复制
z=np.array([np.random.standard_normal() for _ in range(paths)])

代码语言:javascript
复制
z = np.random.standard_normal(paths)

使运行时缩短到2.43秒。改变

代码语言:javascript
复制
ones=[1 for _ in range(len(X))]

代码语言:javascript
复制
ones = np.ones(len(X))

将运行时进一步减少到2.21秒。像这样重新计算Xbasis_funct中的幂

代码语言:javascript
复制
X2 = X**2
X3 = X**3
X4 = X**4
X5 = X**5
...
A = np.column_stack((ones, 1 - X, 1/2 * (2 - 4*X + X2), ...

节省额外100毫秒。改变

代码语言:javascript
复制
itmPaths = [index for index,value in enumerate(K - S) if value > 0]

代码语言:javascript
复制
itmPaths = np.nonzero((K - S) > 0)[0]

使运行时降到大约1.60秒。我认为您现在看到了模式;每当您在numpy数组上循环时,就会有一个numpy内置函数来更好、更快地完成这项工作。

然后在计算rest_paths时:

代码语言:javascript
复制
rest_paths = np.setdiff1d(np.arange(0, paths - 1, 1), exPaths)

这里的paths - 1是故意的吗?在我看来是个虫子。假设它是一个bug,更新P可以更有效,如下所示:

代码语言:javascript
复制
mask = np.zeros(P.shape, dtype = bool)
...
mask.fill(False)
mask[itmPaths[Pt > C]] = True

P[mask] = Pt[Pt > C]
P[~mask] *= e_r_dt

最终代码

修改后的代码在我的计算机上运行约420毫秒。注意,我已经从k == 5函数中删除了除basis_funct 1之外的所有分支。对您来说,将其添加回应该很简单。

代码语言:javascript
复制
import sys
import numpy as np

# Uncomment this line during development to always get the same random
# numbers.
# np.random.seed(1234)

def lsmput(T, r, sigma, K, S0, TimeSteps, paths, k):
    dt = T/TimeSteps
    t = np.arange(0, T+dt, dt).tolist()

    z = np.random.standard_normal(paths)
    w = (r-sigma**2/2)*T + sigma*np.sqrt(T)*z

    S = S0*np.exp(w)
    P = np.maximum(K - S,0)

    e_r_dt = np.exp(-r * dt)
    mask = np.zeros(P.shape, dtype = bool)
    for i in range(TimeSteps-1, -1, -1):
        z = np.random.standard_normal(paths)
        w = t[i]*w/t[i+1] + sigma*np.sqrt(dt*t[i]/t[i+1])*z

        S = S0 * np.exp(w)
        itmPaths = np.nonzero(K > S)[0]
        itmS = S[itmPaths]
        Pt = K - itmS

        itmDiscP = P[itmPaths] * e_r_dt

        A = basis_funct(itmS, k)
        beta = np.linalg.lstsq(A, itmDiscP)[0]
        C = np.dot(A, beta)

        mask.fill(False)
        mask[itmPaths[Pt > C]] = True

        P[mask] = Pt[Pt > C]
        P[~mask] *= e_r_dt
    return np.mean(P * e_r_dt)

def basis_funct(X, k):
    ones = np.ones(len(X))
    assert k == 5
    X2 = X**2
    X3 = X**3
    X4 = X**4
    X5 = X**5
    A = np.column_stack((ones, 1 - X,
                         1/2 * (2 - 4*X + X2),
                         1/6 * (6 - 18*X + 9*X2 - X3),
                         1/24 * (24 - 96*X + 72*X2 - 16*X3 + X4),
                         1/120 * (120 - 600*X + 600*X2 - 200*X3 + 25*X4 - X5)))
    return A

from time import time
start = time()
val = lsmput(1, 0.06, 0.15, 100, 90, 20, 40_000, 5)
print(f'Done in {time() - start:.3} seconds')
print(val)
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/235119

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档