MonteCarlo方法(或MonteCarlo实验)是一大类依赖重复随机抽样获得数值结果的计算算法。基本思想是使用随机性来解决原则上可能是确定性的问题。它们经常用于物理和数学问题,并且在其他方??法困难或不可能时最有用。蒙特卡洛方法主要用于三个不同的问题类别:优化、数值积分和从概率分布生成图。我们将使用蒙特卡洛模拟来观察资产价格随时间的潜在变化,假设它们的每日收益服从正态分布。这种类型的价格演变也被称为“随机游走”。例如,如果我们想购买一只特定的股票,我们可能想尝试展望未来并预测以何种概率可以预期的回报,或者我们可能有兴趣调查哪些潜在的极端结果。我们可能会面临破产风险或在多大程度上面临获得超额回报的风险。为了构建模拟,我们需要估计标的股票的预期回报水平(mu)和波动率(vol)。这些数字可以根据历史价格估算,最简单的方法是假设过去的平均回报率和波动率水平将持续到未来。历史数据也可以根据投资者情绪或市场制度等的变化进行调整,但为了简单起见并专注于代码,我们将根据过去的价格数据设置简单的回报和波动水平。现在让我们开始编写一些代码并生成我们需要的初始数据作为我们的蒙特卡罗模拟的输入。出于说明目的,让我们看看Apple的股票...首先我们必须导入必要的模块,然后开始:#importnecessarypackagesimportnumpyasnpimportmathimportmatplotlib.pyplotaspltfromscipy.statsimportnormfrompandas_datareaderimportdata#downloadApplepricedataintoDataFrameapple=data.DataReader('AAPL','yahoo',start='1/1/2000')#calculatethecompoundannualgrowthrate(CAGR)which#willgiveusourmeanreturninput(mu)days=(apple.index[-1]-apple.index[0]).dayscagr=((((apple['AdjClose'][-1])/apple['AdjClose'][1]))**(365.0/天))-1print('CAGR=',str(round(cagr,4)*100)+"%")mu=cagr#createaseriesofpercentagereturnsandcalculate#theannualvolatilityofreturnsapple['Returns']=apple['AdjClose'].pct_change()vol=apple['Returns'].std()*sqrt(252)print("AnnualVolatility=",str(round(vol,4)*100)+"%")结果如下:CAGR=23.09%AnnualVolatility=42.59%现在我们知道我们的复合年增长率是23.09%,我们的波动率输入(vol)是42.59%——代码到实际上运行蒙特卡洛模拟如下:#DefineVariablesS=apple['AdjClose'][-1]#startingstockprice(即lastavailablerealstockprice)T=252#Numberoftradingdaysmu=0.2309#Returnvol=0.4259#Volatility#createlistofdailyreturnsusingrandomnormaldistributiondaily_returns=np.random.normal((mu/T),vol/math.sqrt(T),T)+1#setstartingpriceandcreatepriceseriesgeneratedbyaboverandomdailyreturnsprice_list=[S]forxindaily_returnsappend:price价格(price_list[-1]*x)#GeneratePlots-priceseriesandhistogramofdailyreturnssplt.plot(price_list)plt.show()plt.hist(daily_returns-1,100)#Notthatwerunthelineplotandhistogramseparately,notsimultaneously.plt.show()这段代码输出图:上面的代码基本上,一个交易年(252天)内标的价格序列演变的单一模拟是基于每日回报的随机抽取,遵循由第一张图表中显示的单线系列表示的正态分布。第二张图表绘制了一年期间这些随机每日回报的直方图。我们现在已经成功地模拟了明年的每日价格数据。但它实际上并没有让我们深入了解股票的风险和回报特征,因为我们只有一条随机生成的路径。实际价格完全按照上表所述变化的可能性几乎为零。那么您可能会问,这种模拟的意义何在?好吧,真正的洞察力是通过运行模拟数千次、数万次甚至数十万次获得的,每次运行都会根据相同的股票特征(mu和vol)产生一组不同的潜在价格演变。我们可以非常简单地调整上面的代码来运行多个模拟。此代码如下所示。在下面的代码中,您会注意到一些事情-首先我删除了直方图(我们稍后会以稍微不同的方式回到这个),现在代码在一个图表上绘制多个价格系列以显示每个价格系列的信息个人模拟运行。importnumpyasnpimportmathimportmatplotlib.pyplotaspltfromscipy.statsimportnorm#DefineVariablesS=apple['AdjClose'][-1]#startingstockprice(i.e.lastavailablerealstockprice)T=252#Numberoftradingdaysmu=0.2309#Returnvol=0.4259#Volatility#choosenumberofrunstosimulate-Ihavechosen1000foriinrange(1000):#createlistofdailyreturnsusingrandomnormaldistributiondaily_returns=np.random.normal(mu/T,vol/math.sqrt(T),T)+1#setstartingpriceandcreatepriceseriesgeneratedbyaboverandomdailyreturnsprice_list=[S]forxindaily_returns:price_list.append(price_list[-1]*x)#plotdatafromeachindividualrunwhichwewillplotattheendplt.plot(price_list)#showtheplotofmultipleboveppriceseriescreated广告.show()这为我们提供了以下1000个不同模拟价格系列的图表:现在我们可以看到1000个不同模拟产生的潜在结果,所有这些都基于相同的基础输入。最终价格相差很大,从大约45美元到500美元不等!在当前的格式中,很难真正清楚地看到发生了什么,因为图表中充满了数据——所以我们回到之前删除的直方图的地方,尽管这次它会向我们展示模拟值的最终分布,而不是单个模拟的每日回报分布。这次我也模拟了10000次运行,给我们更多的数据。同样,代码很容易调整以包含此直方图。importnumpyasnpimportmathimportmatplotlib.pyplotaspltfromscipy.statsimportnorm#setupemptylisttoholdourendingvaluesforeachsimulatedpriceseriesresult=[]#DefineVariablesS=apple['AdjClose'][-1]#startingstockprice(i.e.lastavailablerealstockprice)T=252#Numberoftradingdaysmu=0.2309#Returnvol=0.4259#Volatility#choosenumberofrunstosimulate-Ihavechosen10,000foriinrange(10000):#createlistofdailyreturnsusingrandomnormaldistributiondaily_returns=np.random.normal(mu/T,vol/math.sqrt(T),T)+1#setstartingpriceandcreatepriceseriesgeneratedbyaboverandomdailyreturnsprice_list=[S]forxindaily_returns:price_list.append(price_list[-1]*x)#plotdatafromeachindividualtheendpwhichwewillplotat.plot(price_list)#appendtheendingvalueofeachsimulatedruntotheemptylistwecreatedatthebeginningresult.append(price_list[-1])#showtheplotofmultiplepriceseriescreatedaboveplt.show()#createhistogramofendingstockvaluesforourmutliplesimulationsplt.hist(result,bins=50)plt.show()输出如下:我们现在可以很快计算分布的平均值以获得我们的“预期值”:#usenumpymeanfunctiontocalculatethemeanoftheresultprint(round(np.mean(result),2))输出188.41当然,您会得到略有不同的结果,因为这些是随机的每日回报抽奖模拟。您在每次模拟中包含的路径或运行次数越多,平均值就越倾向于我们用作“mu”输入的平均回报。这是大数定律的结果。我们还可以查看标的价格分布的几个“分位数”,以了解回报率非常高或非常低的可能性。我们可以使用numpy的“percentile”函数来计算5%和95%分位数:print("5%quantile=",np.percentile(result,5))print("95%quantile=",np.percentile(result,95))5%quantile=85.0268905204829495%quantile=344.5558966477557我们现在知道,该股有5%的几率最终会低于85.02美元,有5%的几率会高于344.55美元。我们可以开始问自己这样的问题:“我是否愿意冒5%的风险购买价值低于63.52美元的股票,以追逐188.41美元左右的预期回报?”绘制我们刚刚计算的两个分位数,以直观地表示。扫描本文底部二维码,获取所有完整源码和打包下载的JupyterNotebook文件。plt.hist(结果,bins=100)plt.axvline(np.percentile(result,5),color='r',linestyle='dashed',linewidth=2)plt.axvline(np.percentile(result,95)),color='r',linestyle='虚线',linewidth=2)plt.show()
