用Python中的蒙特卡洛模拟预测股票收益

开发 后端
我们将使用蒙特卡洛模拟来观察资产价格随时间的潜在变化,假设它们的每日收益服从正态分布。这种类型的价格演变也称为“随机游走”(random walk)。

 [[418609]]

蒙特卡洛方法(或蒙特卡洛实验)是一大类计算算法,它们依赖于重复随机采样来获得数值结果。基本思想是使用随机性来解决原则上可能是确定性的问题。它们通常用于物理和数学问题,并且在难以或不可能使用其他方法时最有用。Monte Carlo 方法主要用于三个不同的问题类别:优化、数值积分和从概率分布中生成绘图。

我们将使用蒙特卡洛模拟来观察资产价格随时间的潜在变化,假设它们的每日收益服从正态分布。这种类型的价格演变也称为“随机游走”(random walk)。

例如,如果我们想购买一只特定的股票,我们可能想尝试展望未来并预测可以以何种概率期望得到何种回报,或者我们可能有兴趣调查哪些潜在的极端结果。我们可能会经历以及我们面临破产风险的程度,或者另一方面,获得超额回报的风险。

为了建立模拟,我们需要估计相关股票的预期回报水平 (mu) 和波动率 (vol)。这些数据可以从历史价格中估计出来,最简单的方法是假设过去的平均回报和波动率水平将持续到未来。还可以调整历史数据以考虑投资者观点或市场制度变化等,但是为了保持简单并专注于代码,我们将根据过去的价格数据设置简单的回报和波动率水平。

现在让我们开始编写一些代码并生成我们需要的初始数据作为我们蒙特卡罗模拟的输入。出于说明目的,让我们看看苹果公司的股票......

首先我们必须导入必要的模块,然后开始: 

  1. #import necessary packages  
  2. import numpy as np  
  3. import math  
  4. import matplotlib.pyplot as plt  
  5. from scipy.stats import norm  
  6. from pandas_datareader import data  
  7. #download Apple price data into DataFrame  
  8. apple = data.DataReader('AAPL', 'yahoo',start='1/1/2000' 
  9. #calculate the compound annual growth rate (CAGR) which   
  10. #will give us our mean return input (mu)  
  11. days = (apple.index[-1] - apple.index[0]).days  
  12. cagr = ((((apple['Adj Close'][-1]) / apple['Adj Close'][1])) ** (365.0/days)) - 1  
  13. print ('CAGR =',str(round(cagr,4)*100)+"%")  
  14. mu = cagr  
  15. #create a series of percentage returns and calculate   
  16. #the annual volatility of returns  
  17. apple['Returns'] = apple['Adj Close'].pct_change()  
  18. vol = apple['Returns'].std()*sqrt(252)  
  19. print ("Annual Volatility =",str(round(vol,4)*100)+"%") 

结果如下: 

  1. CAGR = 23.09%  
  2. Annual Volatility = 42.59% 

现在我们知道我们的复合年均增长率是 23.09%,我们的波动率输入 (vol) 是 42.59%——实际运行蒙特卡罗模拟的代码如下: 

  1. #Define Variables  
  2. S = apple['Adj Close'][-1] #starting stock price (i.e. last available real stock price)  
  3. T = 252 #Number of trading days  
  4. mu = 0.2309 #Return  
  5. vol = 0.4259 #Volatility  
  6. #create list of daily returns using random normal distribution  
  7. daily_returns=np.random.normal((mu/T),vol/math.sqrt(T),T)+1  
  8. #set starting price and create price series generated by above random daily returns  
  9. price_list = [S]  
  10. for x in daily_returns:  
  11.     price_list.append(price_list[-1]*x)  
  12. #Generate Plots - price series and histogram of daily returns  
  13. plt.plot(price_list)  
  14. plt.show()  
  15. plt.hist(daily_returns-1, 100) #Note that we run the line plot and histogram separately, not simultaneously.  
  16. plt.show() 

此代码输出绘图:

上面的代码基本上运行了一个交易年度(252 天)内潜在价格序列演变的单一模拟,基于遵循正态分布的每日收益随机的抽取。由第一个图表中显示的单线系列表示。第二个图表绘制了一年期间这些随机每日收益的直方图。

现在我们已经成功模拟了未来一年的每日价格数据。但实际上它并没有让我们深入了解股票的风险和回报特征,因为我们只有一个随机生成的路径。实际价格完全按照上表所述演变的可能性几乎为零。

那么你可能会问这个模拟有什么意义呢?好吧,真正的洞察力是通过运行数千次、数万次甚至数十万次模拟获得的,每次运行都会根据相同的股票特征(mu 和 vol)产生一系列不同的潜在价格演变。

我们可以非常简单地调整上面的代码来运行多个模拟。此代码如下所示。在下面的代码中,您会注意到一些事情——首先我删除了直方图(我们稍后会以稍微不同的方式回到这个问题),并且代码现在在一个图表上绘制多个价格系列以显示信息对于每个单独的模拟运行。 

  1. import numpy as np  
  2. import math  
  3. import matplotlib.pyplot as plt  
  4. from scipy.stats import norm  
  5. #Define Variables  
  6. S = apple['Adj Close'][-1] #starting stock price (i.e. last available real stock price)  
  7. T = 252 #Number of trading days  
  8. mu = 0.2309 #Return  
  9. vol = 0.4259 #Volatility  
  10. #choose number of runs to simulate - I have chosen 1000  
  11. for i in range(1000):  
  12.     #create list of daily returns using random normal distribution  
  13.     daily_returns=np.random.normal(mu/T,vol/math.sqrt(T),T)+1  
  14.     #set starting price and create price series generated by above random daily returns  
  15.     price_list = [S]  
  16.     for x in daily_returns:  
  17.         price_list.append(price_list[-1]*x)  
  18.     #plot data from each individual run which we will plot at the end 
  19.     plt.plot(price_list)  
  20. #show the plot of multiple price series created above  
  21. plt.show() 

这为我们提供了以下 1000 个不同模拟价格系列的图:

现在我们可以看到 1000 次不同模拟产生的潜在结果,考虑到每日收益序列的随机性,所有模拟都基于相同的基本输入。

最终价格的差价相当大,从大约 45 美元到 500 美元不等!

在当前的格式中,由于图表中充满了数据,因此很难真正清楚地看到正在发生的事情——所以这就是我们回到之前删除的直方图的地方,尽管这次它会向我们展示 结束模拟值的分布,而不是单个模拟的每日收益分布。这次我还模拟了 10,000 次运行,以便为我们提供更多数据。

同样,代码很容易调整以包含此直方图。 

  1. import numpy as np  
  2. import math  
  3. import matplotlib.pyplot as plt  
  4. from scipy.stats import norm  
  5. #set up empty list to hold our ending values for each simulated price series  
  6. result = []  
  7. #Define Variables  
  8. S = apple['Adj Close'][-1] #starting stock price (i.e. last available real stock price)  
  9. T = 252 #Number of trading days  
  10. mu = 0.2309 #Return  
  11. vol = 0.4259 #Volatility  
  12. #choose number of runs to simulate - I have chosen 10,000  
  13. for i in range(10000):  
  14.     #create list of daily returns using random normal distribution  
  15.     daily_returns=np.random.normal(mu/T,vol/math.sqrt(T),T)+1  
  16.     #set starting price and create price series generated by above random daily returns  
  17.     price_list = [S]  
  18.     for x in daily_returns:  
  19.         price_list.append(price_list[-1]*x)  
  20.     #plot data from each individual run which we will plot at the end  
  21.     plt.plot(price_list)  
  22.     #append the ending value of each simulated run to the empty list we created at the beginning  
  23.     result.append(price_list[-1])  
  24. #show the plot of multiple price series created above  
  25. plt.show()  
  26. #create histogram of ending stock values for our mutliple simulations  
  27. plt.hist(result,bins=50 
  28. plt.show() 

输出如下:

我们现在可以快速计算分布的平均值以获得我们的“预期值”: 

  1. #use numpy mean function to calculate the mean of the result  
  2. print(round(np.mean(result),2)) 

输出188.41。当然,您会得到略有不同的结果,因为这些是随机每日回报抽取的模拟。您在每次模拟中包含的路径或运行次数越多,平均值就越倾向于我们用作“mu”输入的平均回报。这是大数定律的结果。

我们还可以查看潜在价格分布的几个“分位数”,以了解非常高或非常低回报的可能性。

我们可以使用 numpy 的“percentile”函数来计算 5% 和 95% 的分位数: 

  1. print("5% quantile =",np.percentile(result,5))  
  2. print("95% quantile =",np.percentile(result,95))  
  3. 5% quantile = 85.02689052048294  
  4. 95% quantile = 344.5558966477557 

我们现在知道,股价有 5% 的可能性最终会低于 85.02 美元,有 5% 的可能性会高于 344.55 美元。

我们可以开始问自己这样的问题:“我是否愿意冒 5% 的风险最终获得价值低于 63.52 美元的股票,以追逐股价在 188.41 美元左右的预期回报?”

最后要做的就是在直方图上快速绘制我们刚刚计算的两个分位数,以给我们一个直观的表示。扫描本文最下方二维码获取全部完整源码和Jupyter Notebook 文件打包下载。 

  1. plt.hist(result,bins=100 
  2. plt.axvline(np.percentile(result,5), color='r'linestyle='dashed'linewidth=2 
  3. plt.axvline(np.percentile(result,95), color='r'linestyle='dashed'linewidth=2 
  4. plt.show() 

  

责任编辑:庞桂玉 来源: Python中文社区
相关推荐

2021-08-21 14:30:58

机器学习bilibili股价

2021-08-23 11:15:20

Python机器学习bilibili

2021-11-05 09:01:15

Python算法Python基础

2021-11-08 08:51:36

Python算法Python基础

2023-04-12 15:58:58

2024-05-09 08:33:33

2024-08-19 08:45:00

开源模型

2022-02-18 10:34:19

逻辑回归KNN预测

2022-12-15 16:53:55

2017-11-24 08:00:06

深度学习TensorFlow预测股票

2024-06-17 12:39:37

2023-10-27 13:05:23

模型训练

2021-08-06 09:05:12

Python加密货币脚本

2018-08-23 13:04:48

Python月薪支出

2024-07-10 09:37:57

2024-02-29 11:53:22

神经网络NNVMC偏微分方程求解器

2017-08-21 11:06:54

Unify

2021-09-29 09:35:29

Python典型化事实代码

2021-09-14 10:20:48

Python股票布林带

2019-04-26 13:26:00

预测股票深度学习股票
点赞
收藏

51CTO技术栈公众号