摘要
1:本文讨论将蒙特卡洛模拟的理念运用在市场风险分析上;
2:笔者希望搭建出一套交易体系,原则是只做干货的分享。后续将更新更多模块及内容,但工作学习之余的闲暇时间有限,更新速度慢还请谅解;
3:文中假设与观点是基于笔者对模型及数据的一孔之见,若有不同见解欢迎随时留言交流;
4:本文所涉及数据均通过公开渠道获取,本文所涉内容与项目相关方不存在利益冲突;
5:模型实现基于python3.8;
目录
1. 蒙特卡洛模拟
笔者去年写过一篇蒙特卡洛模拟预测股价的文章,也有不少读者发出质疑,毕竟用蒙特卡洛模拟无规律的布朗运动,得到的不还是一堆无规律的结果,云云。其实笔者只是举个例子,更重要的是介绍蒙特卡洛模拟的理念。毕竟模型需要假设分布,而无规律的布朗运动正好是用来描述股价的方式之一。哪天您要是读到笔者的文章获得了什么灵感可以洞悉市场规律,笔者给您的忠告是:请千万不要告诉任何人,拿着模型去赚钱吧。
蒙特卡洛模拟的理念和方式本文就不再赘述了,网上有很多文章,之前笔者也写过,附传送门:
2. 项目背景和目标
为了弥补下之前股价模型假设的遗憾,笔者出一期关于市场预测的内容,正好最近笔者的项目与之相关,在得到项目方允许的情况下,笔者利用公开数据及部分模型仅作展示,更重要的还是理念的讲解。
该项目是关于澳大利亚大健康领域,数据方面笔者挑选了损伤数据中的运动脑震(Concussion)数据。任务目标是模拟出这该损伤未来五年的市场数据,并且判断出第五年的市场下行风险。
由于当地州政府和卫生局比较懒(官网称目前正在推进国家级运动损伤数据库的建设,等它建好我们就该喝西北风去了),相关数据十分匮乏,给项目推进造成不小的阻碍。并且,进行大范围的市场调查也非常吃力。在这种数据匮乏的时候笔者选择使用蒙特卡洛模拟,其精妙之处在于可以进行一堆高大上的假设和推导(前提是合理公允),其过程看上去无比复杂,充分展现笔者的专业性。但其结果却是通俗易懂的分布展示,即使客户没学过统计,您也可以用几分钟的时间向他展示通俗易懂的分布,充分体现客户关怀。像这种又能体现专(装)业(13),又客户友好型的解决方案那不得用爆哇?
废话不多说,马上开整。
3. 数据分析
在项目一开始时所有人直接定位脑震荡和软组织损伤,因为澳洲医疗条件完备,近几年来所有运动损伤中就脑震荡是所有运动损伤中增长最大的,除此之外其它运动损伤数据近几年都有递减的趋势。而且,这两种损伤医院和政府也更关切,可以说是投其所好了。但笔者提出几个不一样的观查点:
首先是在所有运动损伤中,脑震荡数字增长的确很大,如图一数据:
图一:运动脑震荡统计
数据来源:
虽然其绝对的数额很低,只有几百人,但在澳洲引发的关注度很高,甚至连医院的数据集都要给脑震荡单开好几张统计表。
那么:
为什么其它的运动损伤都在递减, 就脑震荡众人皆跌它独涨呢?因为澳洲人喜欢踢足球。
为什么喜欢踢足球脑震荡数字会涨呢?因为为人脑在颅腔内像块豆腐一样很脆弱,研究表明头球,冲撞等因素都是脑震荡的元凶,感兴趣的可以了解一下脑震荡,有很多相关的文章和研究。
为什么关注度这么高呢?因为球迷们喜欢的球星得了脑震荡,害得退赛了球迷那不得疯狂diss它。
好家伙,一波为什么三连直接把笔者调查好长时间的结果全说完了,令人唏嘘不已。。毕竟这不是重点,来看看数据吧:
图二:澳大利亚足球运动参与人数与年化增速
数据来源:
可以看到,澳洲踢足球的疫情前大概2百万人,也就是说大概十个澳洲人里有一个是踢球的。(不知道我们国家是多少,反正笔者是纯粹不踢球的人)。
从增速上看,即使排除疫情这两年的异常数据,增速也是一直在下滑的。原因无非是澳洲人口总量的限制,总有一个天花板在。因此,以足球数据反推脑震荡数据,将来想预期脑震荡市场一直这样高增长下去非常不现实。况且随着社会的发展,医疗条件只会越来越进步, 更好的医疗条件只会让脑震荡案例进一步下降。
4. 模型推导
在上面分析和数据基础上,笔者对未来五年的脑震荡案例数据进行模型推演。
4.1 足球参与人数模拟
由于脑震荡数据与足球参与人数相关性很强, 首先是未来的足球参与人数满足方程[1]:
[1]
其中:
i: 年份
g: 复合增长率(年化)
p:参与人数
就是很简单的复合增速算数值的公式,C站插公式有点迷,上面的是i次根号才对,看上去像乘。
关键就在于这个g,笔者通过时间序列回归拟合过去增速并且加入随机游走项得到, 刨去2021, 2020及2016几年的异常数据,经过拟合可得脑震荡年化复合增速满足方程[2]:
[2]
其中:g为年化复合增速,x为年份
经假设检验,该式在99%置信水平下有效, 。
结合方程[2],在[1]式基础上加入随机游走项有公式[3]:
[3]
其中:服从该随机游走过程, N服从均值为0, 方差为
的正态分布
4.2 脑震荡数据模拟
笔者一开始的方案是照前面足球运动参与人数的方式估计脑震荡数据,但是后面模拟发现效果并不理想。
4.2.1 原方案:线性拟合假设
取2014-2018年脑震荡数据与足球参与人数可得脑震荡比率:
year no. | concussion rate |
1 | 0.051778 |
2 | 0.047775 |
3 | 0.041499 |
4 | 0.035621 |
5 | 0.035373 |
回归拟合可得方程[4]:
[4]
其中:
c:脑震荡比例
x: 年份
经假设检验,该式在99%置信水平下有效, 。
由于脑震荡比例降低,市场趋于稳定,其数据的波动率其实也有随着降低的趋势, 因此这里笔者假设脑震荡的标准差比例随着脑震荡比率的下降而降低。其实这样假设是有点问题的,因为线性拟合的其中一个假设是独立同分布,如果假设方差随时间变化而递减则产生异方差,而且是那种条件异方差,但蒙特卡洛模拟好就好在假设灵活,而且是对未来的估计。笔者认为这么用符合实际情况,依旧是公允的。结合之前的方程[3]加入比例随机游走过程得总方程[5]:
[5]
变量有点多,重新列一下, 服从该随机游走过程, 其中:
i: 年份
g: 复合增长率(年化)
p:参与人数
N服从均值为0, 方差为的正态分布
C服从均值为0, 方差为 的正态分布
除了假设外,从公式看这个方案效果依旧不是很理想,因为它是线性时间序列数据拟合,样本点也非常少,这导致在模拟很多期带入公式后产生负值,而这个比率是不可能为负的,于是这个方案在项目上就被放弃了,不过笔者在下面代码实现中还是把它运行出来,毕竟也是花了几行代码实现出来的,在文章里发光发热一下。
4.2.2 新方案:正态分布假设
假设脑震荡比率服从正态分布, 直接在分布里面取数然后乘到之前足球人数里, 于是有总方程[6]:
[6]
这个方案简洁明了,笔者就不在下面代码中实现了。
5. 代码实现
将上述过程以代码实现:
import pandas as pd
import numpy as np
import random
import matplotlib.pyplot as plt
import seaborn as sns
def player_simulation(years, beg, sigma, player_sim): # just for football player
years_data = []
for i in range(0, years):
if player_sim is True: # football player number simulation
g = 0.217433 - 0.01738 * (1 + i) + random.normalvariate(0, sigma/(i+1))
compunded_g = (1 + g) ** (1 + i)
years_data.append(beg * compunded_g)
else: # 原脑震荡比例线性拟合模拟方案 concussion number simulation
proportion = 0.055899 - 0.0045 * (1 + i) + random.normalvariate(0, sigma/(i+1))
years_data.append(proportion/100)
return years_data
df = pd.read_excel("file.xlsx", sheet_name="name") # 导入本地数据集
player_data = df[df.columns[1]] # 运动员数据
growth = []
for i in range(len(player_data)-1):
growth.append((1 + (player_data[i+1]-player_data[0])/player_data[0])**(1/(i+1)) - 1) # compunded g: (1+g)^(1/year no.) - 1
std_player_g, mean_player_g = np.std(growth[:5]), np.mean(growth[:5]) # 后面两年COVID异常数据不要
concussion_rate_data = pd.read_excel("file.xlsx", sheet_name="name") # 导入本地数据集
concussion_ratio = concussion_rate_data["concussion_rate_in_football_players"].values[5:8] # 笔者只用16-18年数据, 其它drop掉
std_c_r, mean_c_r = np.std(concussion_ratio), np.mean(concussion_ratio)
players_simu = [] # 脑震荡运动员数量模拟结果
for times in range(200000):
player_simu = player_simulation(5, player_data[len(player_data)-1], std_player_g, player_sim = True)
c_simu = player_simulation(5, concussion_ratio[len(concussion_ratio)-1], std_c_r, player_sim = False)
players_simu.append(np.multiply(player_simu, c_simu))
print("No.{} simulation\r".format(times), end="")
print("simulation success")
将模拟数据展示出来,可以选择动态展示(plot动态画线,将plt.show()去掉,在for循环中加入下面两句就可以动态展示)。
plt.draw()
plt.pause(0.001)
但模拟上万次的线要画很长时间,属于给别人展示时候的耍酷功能, 如果模拟几十上百万次估计睡一觉明天起来看还在画线呢。
也可以一次性展示出来, 快,但是几十万次模拟也要几十秒才能刷出来。
selector = input("Draw simulation lines? (y/n):") # 要不要展示作图过程
fig1 = plt.figure(1)
if (selector == "Y") or (selector == "y"):
for i in range(len(players_simu)):
plt.plot([2022,2023,2024,2025,2026], players_simu[i])
plt.xticks([2022,2023,2024,2025,2026])
plt.xlabel("years")
plt.ylabel("Number")
plt.draw()
plt.pause(0.001)
plt.close(fig1)
elif (selector == "N") or (selector == "n"):
for i in range(len(players_simu)):
plt.plot([2022, 2023, 2024, 2025, 2026], players_simu[i])
plt.xticks([2022, 2023, 2024, 2025, 2026])
plt.xlabel("years")
plt.ylabel("E(No.)")
plt.show()
else:
print("Error instruction, input y or n, please run again")
最后一年的分布也可以导出:
last_y = []
for i in range(len(players_simu)):
last_y.append(players_simu[i][-1])
sns.distplot(last_y)
plt.xlabel("E(No.)")
plt.show()
6. 结果展示及讨论
图三:蒙特卡洛模拟(20万次)
图四:蒙特卡洛模拟第五年分布情况(20万次)
可以看到,脑震荡在未来5年中呈现先扬后抑的走势。由于疫情好转,可以说未来几年内市场还是比较安全的。但在市场到达顶点之前就要提前考虑第二增长曲线,提早进行部署。从最后一年的分布来看,市场虽然会经历下滑,但总量上依旧维持在一个历史较高的水平。值得注意的是有10%的情况下,市场将下滑至低于848例脑震荡患者。
可以看到,这一通分析还是很有价值的,只是损伤数据岂止脑震荡那么简单,很多数据也很不完善,实务中仍然有一堆问题要解决不过模拟方法大同小异。
市场分析对基本面分析依旧意义重大,市场是水,公司是船,水涨自然船高,无水自然搁浅,而蒙特卡洛模拟用简单的分布告诉我们未来的水位究竟会如何变化,其实在笔者看来数据缺少是可以克服的困难,因此不算是困难,此外,代码不是困难,建模不是困难,分析也不是困难,真正困难的是合理公允的假设,这是技术活中的技术活。
7. 本文所用到的脑震荡数据集
由于数据及其少,笔者简单po这里了, 链接在前文放了,这里就不放了:
澳洲足球运动参与人数:
year | No. of football players in AU |
2014 | 990759 |
2015 | 1188911 |
2016 | 1301244 |
2017 | 1631041 |
2018 | 1851683 |
2019 | 1957552 |
2020 | 1181931 |
2021 | 1421804 |
足球运动脑震荡人数:
Year | Concussion No. |
2011 | 346 |
2012 | 334 |
2013 | 417 |
2014 | 513 |
2015 | 568 |
2016 | 540 |
2017 | 581 |
2018 | 655 |
您若不弃,我们风雨共济!