自定义利率模拟函数的输出结果不符合预期

下面是我自定义的一个名为vasicek_sim的函数用来模拟一段时间内利率的变化路径。

def vasicek_sim(initial, final_time, sim_path_len, num_paths=None, *,
                a, b, sigma, allow_neg=True):
    """

    Parameters
    ----------
    inital : float
        the initial value of interest rate, i.e. r(0).
    final_time : int
        the time when the simulation stops.
    sim_path_len : int
        the number of points generated after the initial data.
    num_paths : int, optional
        the number of paths simulated. The default is None.
    a : float
        the parameter used in corresponding interest rate model.
    b : float
        the parameter used in corresponding interest rate model.
    sigma : float
        the parameter used in corresponding interest rate model.
    allow_neg : bool, optional
        when it equals to True, a negative interest rate is allowable
        in the simulation. The default is True.

    Returns
    -------
    None.

    """
    import math, random
    """"Create a list to store a path of interest rate(IR)"""
    if type(initial) == list:
        path_initial = initial
    else:
        path_initial = [initial]
    delta = final_time / sim_path_len
    """" Generate a original path_box which has num_paths identical initial."""
    if num_paths == None:
        num_paths = 1
    path_box = [path_initial for i in range(num_paths)]
    y = [] # Used to store the paths of IR that have negative IR.
    exp = math.exp(-a*delta)
    """" When num_paths is default value, there is only one path."""
    for i in path_box:
        for j in range(sim_path_len):
            #print(i[-1])
            r_old = i[-1]
            W_1 = random.gauss(0, 1)
            W_2 = random.gauss(0, 1)
            #print(f'{W_1 = }\n{W_2 = }')
            r_new = (exp*r_old
                     + b*(1-exp)
                     + sigma*((1-exp**2)/(2*a))**0.5*(W_2-W_1))
            if allow_neg == False and r_new < 0:
                print(r_new)
                break
            #print(r_new)
            #print(i)
            i.append(r_new)
    # The paths removed before are appended to path_box again.
    if y != []:
        path_box.append(y)
    return path_box

下图是我的测试代码

if __name__ == '__main__':
    z = vasicek_sim([0.25, 0.3, 0.4], 2, 3, 2, a=0.5, b=0.6, sigma=0.2)
    print(f'The path of interest rate is\n {z}')

最后是我的运行结果

The path of interest rate is
 [[0.25, 0.3, 0.4, 0.3479323841038564, 0.2375140125192349, 0.40864261608808583, 0.5588591529202444, 0.3464802076221359, 0.2132402884958335], [0.25, 0.3, 0.4, 0.3479323841038564, 0.2375140125192349, 0.40864261608808583, 0.5588591529202444, 0.3464802076221359, 0.2132402884958335]]

预期结果是,返回的path_box中的两个子列表各包含6个元素,其中前三个相同,后三个不同,但运行后发现每个子列表各有9个元素,并且两个子列表完全一样。通过检查发现,第一个子列表的最后三个元素本应该储存在第二个子列表的[3:5]部分,同时,debug的过程中每次最里面的回圈结束一圈后,path_box的两个子列表会同时被改变而非只改变当前这一个。

请问是哪部分代码导致了这个问题?

讨论数量: 4
Jason990420

If nothing wrong, it is caused by following statement.

path_box = [path_initial for i in range(num_paths)]

The items in list, path_box, will be all referenced to same variable path_initial, needed to be done shallow copy, like

path_box = [path_initial.copy() for i in range(num_paths)]

then you will get the expected result

[
    [0.25, 0.3, 0.4, 0.7714668192797642, 0.4566794244275462, 0.6019010230728543],
    [0.25, 0.3, 0.4, 0.41725434747677215, 0.3613226425994896, 0.5180695456476787],
]
2个月前 评论
Ivanharry (楼主) 2个月前
Jason990420
a = [1, 2, 3]
b = a
c = a.copy()

file

a[0] = 0
print(a)
print(b)
print(c)
[0, 2, 3]
[0, 2, 3]
[1, 2, 3]

file

2个月前 评论

这个vasicek_sim函数是用来模拟Vasicek利率模型的。Vasicek模型是一种常用于金融衍生品定价的短期利率模型。但是,你的代码中存在一些问题,我将指出并提供修正建议。

首先,我们来看代码的目的:

模拟Vasicek利率模型的一条或多条路径。 允许用户指定是否允许负利率。 现在,我们分析代码中的问题:

变量y被初始化为空列表,但在后面的代码中并没有被使用。看起来你可能想用它来存储那些产生负利率的路径,但实际上并没有这么做。 在模拟路径时,如果allow_neg为False并且产生了负利率,你只是打印了这个负利率并跳出了内层循环。这并不会停止对整个path_box的遍历,也不会将这条路径添加到y列表中。 你的代码中使用了两个高斯随机数W_1和W_2,但在Vasicek模型的离散化版本中通常只需要一个。这可能是个错误。 你的代码没有处理num_paths为None的情况,尽管你在函数签名中指定了默认值为None。但实际上,你在函数一开始就检查了它的值,并为其分配了默认值1。为了更清晰,你应该在函数签名中直接为其指定默认值。 注释中的“Create a list to store a path of interest rate(IR)”和其他类似的注释与代码行之间应该有一个空行,以符合Python的PEP 8风格指南。 下面是对代码的修正:

python import math
import random

def vasicek_sim(initial, final_time, sim_path_len, num_paths=1, a=None, b=None, sigma=None, *, allow_neg=True):
"""
Simulate paths using the Vasicek interest rate model.

Parameters  
----------  
initial : float or list of float  
    The initial value(s) of the interest rate.  
final_time : float  
    The time when the simulation stops.  
sim_path_len : int  
    The number of time steps in each simulation path.  
num_paths : int, optional  
    The number of paths to simulate. Default is 1.  
a, b, sigma : float, required  
    The parameters of the Vasicek model. These should not be optional.  
allow_neg : bool, optional  
    Whether to allow negative interest rates in the simulation. Default is True.  

Returns  
-------  
list of list of float  
    A list containing the simulated paths.  
"""  
if a is None or b is None or sigma is None:  
    raise ValueError("Parameters a, b, and sigma are required for the Vasicek model.")  

if isinstance(initial, list):  
    path_initial = initial  
else:  
    path_initial = [initial] * num_paths  

delta = final_time / sim_path_len  
exp = math.exp(-a * delta)  
path_box = [path_initial]  # Start with a list of initial values for each path  

for _ in range(sim_path_len):  
    new_rates = []  
    for r_old in path_box[-1]:  # Take the latest rates from the path_box  
        W = random.gauss(0, 1)  # Generate a single Gaussian random variable  
        r_new = (exp * r_old  
                 + b * (1 - exp)  
                 + sigma * ((1 - exp**2) / (2 * a))**0.5 * W)  
        if allow_neg or r_new >= 0:  
            new_rates.append(r_new)  
        else:  
            # Handle negative rates based on the allow_neg flag  
            if allow_neg:  
                new_rates.append(r_new)  # Keep the negative rate if allowed  
            else:  
                new_rates.append(0)  # Replace with zero or handle this case differently  
    path_box.append(new_rates)  

return path_box

注意:在这个修正版本中,我改变了函数的一些行为。例如,我现在允许initial是一个列表,这样你可以为每条路径指定一个不同的初始利率。我还添加了错误处理来确保a、b和sigma参数被提供。最后,我改变了如何处理负利率的情况。如果allow_neg为False并且产生了负利率,我现在将其替换为0(但你可能想以不同的方式处理这种情况)。

2个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!