打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
python金融数据科学:贝叶斯统计

  近年来,贝叶斯统计已经成为实证金融学的基石。本章无法覆盖该领域的所有概念。因此,在必要时应该参考Geweke(2005)的教科书来了解一般的入门知识,利益驱动者应该参考Rachev(2008)的教科书。

  13.3.1 贝叶斯公式

  贝叶斯公式在金融学中常见的解读是历时解读。这主要说明,随着时间的推移,我们会了解感兴趣的变量或者参数的新信息,例如时间序列的平均收益率。公式13-5正式描述了这种理论。

  公式13-5 贝叶斯公式

  

  式中H表示某个事件(假设),D代表实验或者真实世界可能提供的数据[4]。在这些定义的基础上,我们得到:

  p(H)称作先验概率;p(D)是任何假设下数据的概率,称作标准化常数;p(D|H)是假设H下数据的似然度(即概率);p(H|D)是后验概率,即我们看到数据之后得出的概率。

  考虑一个简单的例子。有两个盒子B1和B2,B1中有20个黑球和70个红球,而B2中有40个黑球和50个红球。随机从两个盒子中取出一个球,假定这个球是黑色的。“H1:球来自B1”和“H2:球来自B2”这两个假设的概率分别是多少?

  在随机取出这个球之前,两个假设的可能性是相同的。但当球明显是黑色,我们必须根据贝叶斯公式更新两个假设的概率,考虑假设H1。

  先验概率:p(H1)=1/2标准化常数:p(D)=似然度:p(D|H1)=

  更新后的H1概率p(H1|D)=

  

  这一结果也有直观的意义。从B2取出一个黑球的概率两倍于同一件事情发生在B1上的概率。因此,取出一个黑球之后,假设H2的更新后概率p(H2|D)=2/3,也是假设H1更新后概率的2倍。

  13.3.2 贝叶斯回归

  Python生态系统利用PyMC3提供了一个全面的软件包,从技术上实现了贝叶斯统计和概率规划。

  考虑如下基于直线周围噪声数据的例子。[5]首先,在数据集上实施一次线性普通最小二乘回归(参见第11章),结果如图13-15所示:

  

  图13-15 样本数据点和回归线

  In [1]: import numpy as np

  import pandas as pd

  import datetime as dt

  from pylab import mpl, plt

  In [2]: plt.style.use('seaborn')

  mpl.rcParams['font.family']='serif'

  np.random.seed(1000)

  %matplotlib inline

  In [3]: x=np.linspace(0, 10, 500)

  y=4 + 2 * x + np.random.standard_normal(len(x)) * 2

  In [4]: reg=np.polyfit(x, y, 1)

  In [5]: reg

  Out[5]: array([2.03384161, 3.77649234])

  In [6]: plt.figure(figsize=(10, 6))

  plt.scatter(x, y, c=y, marker='v', cmap='coolwarm')

  plt.plot(x, reg[1] + reg[0] * x, lw=2.0)

  plt.colorbar()

  plt.xlabel('x')

  plt.ylabel('y')

  OLS回归方法的结果是回归线上两个参数(截距和斜率)的固定值。注意,最高阶单项式因子(本例中是回归线的斜率)在指数水平0处,截距在指数水平1处。原始参数2和4没能完全恢复,这是数据中包含的噪声所致。

  贝叶斯回归利用了PyMC3软件包。这里假定参数遵循某种分布。例如,考虑回归线方程

  

  。假定现在有以下先验概率:

  α呈正态分布,均值为0,标准差为20;β呈正态分布,均值为0,标准差为10。

  至于似然度,假定一个均值为

  

  的正态分布和一个标准差为0~10之间的均匀分布。

  贝叶斯回归的一个要素是(马尔科夫链)蒙特卡洛(MCMC)采样[6]。原则上,这和前一个例子中多次从盒子取出球相同——只是采用更系统性、更自动化的方式。

  技术性采样有3个不同的函数可以调用:

  find_MAP()通过求取局部最大后验点来寻找采样算法的起点;NUTS()为假定先验概率的MCMC采样实现所谓的“高效双平均无回转采样器”(NUTS)算法;sample()以给定起始值(来自find_MAP())和最优化步长(来自NUTS算法)提取一定数量的样本。

  上列函数都包装到一个PyMC3 Model对象中,并在with语句中执行:

  In [8]: import pymc3 as pm

  In [9]: %%time

  with pm.Model() as model:

  # model

  alpha = pm.Normal('alpha', mu=0, sd=20) ?

  beta = pm.Normal('beta', mu=0, sd=10) ?

  sigma = pm.Uniform('sigma', lower=0, upper=10) ?

  y_est = alpha + beta * x ?

  likelihood = pm.Normal('y', mu=y_est, sd=sigma,

  observed=y) ?

  # inference

  start = pm.find_MAP() ?

  step = pm.NUTS() ?

  trace = pm.sample(100, tune=1000, start=start,

  progressbar=True, verbose=False) ?

  logp = -1,067.8, ||grad|| = 60.354: 100%| | 28/28 [00:00<00:00,

  474.70it/s]

  Only 100 samples in chain.

  Auto-assigning NUTS sampler...

  Initializing NUTS using jitter+adapt_diag...

  Multiprocess sampling (2 chains in 2 jobs)

  NUTS: [sigma, beta, alpha]

  Sampling 2 chains: 100%| | 2200/2200 [00:03<00:00,

  690.96draws/s]

  CPU times: user 6.2 s, sys: 1.72 s, total: 7.92 s

  Wall time: 1min 28s

  In [10]: pm.summary(trace) ?

  Out[10]:

  mean sd mc_error hpd_2.5 hpd_97.5 n_eff Rhat

  alpha 3.764027 0.174796 0.013177 3.431739 4.070091 152.446951 0.996281

  beta 2.036318 0.030519 0.002230 1.986874 2.094008 106.505590 0.999155

  sigma 2.010398 0.058663 0.004517 1.904395 2.138187 188.643293 0.998547

  In [11]: trace[0] ?

  Out[11]: {'alpha': 3.9303300798212444,

  'beta': 2.0020264758995463,

  'sigma_interval__': -1.3519315719461853,

  'sigma': 2.0555476283253156}

  ? 定义先验概率。

  ? 指定线性回归。

  ? 定义似然度。

  ? 通过优化找出起始值。

  ? 实例化MCMC算法。

  ? 用NUTS取得后验样本。

  ? 展示采样的统计摘要。

  ? 从第一个样本估算。

  3个估算值都相当接近原始值(4,2,2)。不过,整个过程会得到许多估算值。最好用轨迹图(如图 13-20 所示)描述它们。轨迹图展示了不同参数得到的后验分布以及每个样本的单独估算值。后验分布帮助我们直观地了解了估算值中的不确定性:

  In [12]: pm.traceplot(trace, lines={'alpha': 4, 'beta': 2, 'sigma': 2});

  图13-16 后验分布及轨迹图

  仅从回归中取得alpha和beta值就可以绘制富贵所有的结果回归线(见图13-17):

  In [13]: plt.figure(figsize=(10, 6))

  plt.scatter(x, y, c=y, marker='v', cmap='coolwarm')

  plt.colorbar()

  plt.xlabel('x')

  plt.ylabel('y')

  for i in range(len(trace)):

  plt.plot(x, trace['alpha'][i] + trace['beta'][i] * x) ?

  ? 绘制单条回归线。

  

  图13-17 基于不同估算值的回归线

  13.3.3 两种金融工具

  用虚拟数据介绍了PyMC3贝叶斯回归之后,转向真实的金融数据很简单。示例使用了两种交易型开放式基金(GLD和GDX)的金融时间序列数据(见图13-18):

  In [14]: raw=pd.read_csv('../../source/tr_eikon_eod_data.csv',

  index_col=0, parse_dates=True)

  In [15]: data=raw[['GDX', 'GLD']].dropna()

  In [16]: data=data / data.iloc[0] ?

  In [17]: data()

  DatetimeIndex: 2138 entries, 2010-01-04 to 2022-06-29

  Data columns (total 2 columns):

  GDX 2138 non-null float64

  GLD 2138 non-null float64

  dtypes: float64(2)

  memory usage: 50.1 KB

  In [18]: data.ix[-1] / data.ix[0] - 1 ?

  Out[18]: GDX -0.532383

  GLD 0.080601

  dtype: float64

  In [19]: data.corr() ?

  Out[19]: GDX GLD

  GDX 1.00000 0.71539

  GLD 0.71539 1.00000

  In [20]: data.plot(figsize=(10, 6));

  ? 将数据规范化为起始值1。

  ? 计算相对绩效。

  ? 计算两种金融工具之间的相关度。

  

  图13-18 GLD和GDX一段时间后的规范化价格

  在下面的例子中,单个数据点的日期用散点图来可视化。为此,将DataFrame中的DatetimeIndex对象转换成matplotlib日期。图13-19展示了时间序列数据的散点图,并将GLD价值与GDX价值进行对照,然后以不同颜色显示每对数据的日期:[7]

  In [21]: data.index[:3]

  Out[21]: DatetimeIndex(['2010-01-04', '2010-01-05', '2010-01-06'],

  dtype='datetime64[ns]', name='Date', freq=None)

  In [22]: mpl_dates=mpl.dates.date2num(data.index.to_pydatetime()) ?

  mpl_dates[:3]

  Out[22]: array([733776., 733777., 733778.])

  In [23]: plt.figure(figsize=(10, 6))

  plt.scatter(data['GDX'], data['GLD'], c=mpl_dates,

  marker='o', cmap='coolwarm')

  plt.xlabel('GDX')

  plt.ylabel('GLD')

  plt.colorbar(ticks=mpl.dates.DayLocator(interval=250),

  format=mpl.dates.DateFormatter('%d %b %y')); ?

  ? 将DatetimeIndex对象转换为matplotlib日期。

  ? 自定义日期的色卡。

  

  图13-19 GLD价格与GDX价格散点图

  接下来在这两个时间序列基础上实施贝叶斯回归,参数化本质上和前一个虚拟数据示例的过程相同。图13-20展示了在3个参数先验概率分布的假设下,MCMC采样过程的结果:

  In [24]: with pm.Model() as model:

  alpha = pm.Normal('alpha', mu=0, sd=20)

  beta = pm.Normal('beta', mu=0, sd=20)

  sigma = pm.Uniform('sigma', lower=0, upper=50)

  y_est = alpha + beta * data['GDX'].values

  likelihood = pm.Normal('GLD', mu=y_est, sd=sigma,

  observed=data['GLD'].values)

  start = pm.find_MAP()

  step = pm.NUTS()

  trace = pm.sample(250, tune=2000, start=start,

  progressbar=True)

  logp = 1,493.7, ||grad|| = 188.29: 100%| | 27/27 [00:00<00:00,

  1609.34it/s]

  Only 250 samples in chain.

  Auto-assigning NUTS sampler...

  Initializing NUTS using jitter+adapt_diag...

  Multiprocess sampling (2 chains in 2 jobs)

  NUTS: [sigma, beta, alpha]

  Sampling 2 chains: 100%| | 4500/4500 [00:09<00:00,

  465.07draws/s]

  The estimated number of effective samples is smaller than 200 for some

  parameters.

  In [25]: pm.summary(trace)

  Out[25]:

  mean sd mc_error hpd_2.5 hpd_97.5 n_eff Rhat

  alpha 0.913335 0.005983 0.000356 0.901586 0.924714 184.264900 1.001855

  beta 0.385394 0.007746 0.000461 0.369154 0.398291 215.477738 1.001570

  sigma 0.119484 0.001964 0.000098 0.115305 0.123315 312.260213 1.005246

  In [26]: fig = pm.traceplot(trace)

  图13-20 GDX和GLD数据的后验分布及轨迹图

  图13-21将得到的所有回归线添加到之前的散点图中。但是,所有回归线相互之间很靠近:

  In [27]: plt.figure(figsize=(10, 6))

  plt.scatter(data['GDX'], data['GLD'], c=mpl_dates,

  marker='o', cmap='coolwarm')

  plt.xlabel('GDX')

  plt.ylabel('GLD')

  for i in range(len(trace)):

  plt.plot(data['GDX'],

  trace['alpha'][i] + trace['beta'][i] * data['GDX'])

  plt.colorbar(ticks=mpl.dates.DayLocator(interval=250),

  format=mpl.dates.DateFormatter('%d %b %y'));

  

  图13-21 穿过GDX和GLD数据的多条贝叶斯回归线

  图 13-21 揭示了所用回归方法的一个重大缺点:该方法没有考虑随时间推移发生的变化。也就是说,最近的数据和最老的数据受到同等对待。

  13.3.4 随时更新估算值

  正如前面指出的,金融学上的贝叶斯方法通常在历史分析时最有用——也就是说,随着时间推移揭示的新数据可以得出更好的回归和估算。

  为了在当前的示例中加入上述概念,假定回归参数不只是随机和呈某种分布的,而且还会随着时间的推移随机“游走”。这和金融理论中从随机变量推广到随机过程(本质上是随机变量的有序序列)时采用相同的方式。

  为此,我们定义一个新的PyMC3模型,这次指定参数值随机游走。在指定随机游走参数的分布之后,我们可以继续指定随机游走的alpha和beta值。为了使整个过程更加高效,为50个数据点使用相同的系数:

  In [28]: from pymc3.distributions.timeseries import GaussianRandomWalk

  In [29]: subsample_alpha=50

  subsample_beta=50

  In [30]: model_randomwalk=pm.Model()

  with model_randomwalk:

  sigma_alpha=pm.Exponential('sig_alpha', 1. / .02, testval=.1) ?

  sigma_beta=pm.Exponential('sig_beta', 1. / .02, testval=.1) ?

  alpha=GaussianRandomWalk('alpha', sigma_alpha ** -2,

  shape=int(len(data) / subsample_alpha)) ?

  beta=GaussianRandomWalk('beta', sigma_beta ** -2,

  shape=int(len(data) / subsample_beta)) ?

  alpha_r=np.repeat(alpha, subsample_alpha) ?

  beta_r=np.repeat(beta, subsample_beta) ?

  regression=alpha_r + beta_r * data['GDX'].values[:2100] ?

  sd=pm.Uniform('sd', 0, 20) ?

  likelihood=pm.Normal('GLD', mu=regression, sd=sd,

  observed=data['GLD'].values[:2100]) ?

  ? 为随机游走参数定义先验概率。

  ? 随机游走模型。

  ? 将参数向量代入时间间隔长度。

  ? 定义回归模型。

  ? 标准差的先验值。

  ? 用回归结果中的mu定义似然度。

  由于使用随机游走代替单一随机变量,所以这些定义都比以前复杂一些。不过,MCMC的推导步骤本质上仍然没有变化。要注意的是,由于我们必须为每个随机游走样本计算一个参数对,共计1950/50=39个(以前只需要1个),所以计算负担明显增大:

  In [31]: %%time

  import scipy.optimize as sco

  with model_randomwalk:

  start = pm.find_MAP(vars=[alpha, beta],

  fmin=sco.fmin_l_bfgs_b)

  step = pm.NUTS(scaling=start)

  trace_rw = pm.sample(250, tune=1000, start=start,

  progressbar=True)

  logp = -6,657: 2%| | 82/5000 [00:00<00:08, 550.29it/s]

  Only 250 samples in chain.

  Auto-assigning NUTS sampler...

  Initializing NUTS using jitter+adapt_diag...

  Multiprocess sampling (2 chains in 2 jobs)

  NUTS: [sd, beta, alpha, sig_beta, sig_alpha]

  Sampling 2 chains: 100%| | 2500/2500 [02:48<00:00, 8.59draws/s]

  CPU times: user 27.5 s, sys: 3.68 s, total: 31.2 s

  Wall time: 5min 3s

  In [32]: pm.summary(trace_rw).head() ?

  Out[32]:

  mean sd mc_error hpd_2.5 hpd_97.5 n_eff \

  alpha__0 0.673846 0.040224 0.001376 0.592655 0.753034 1004.616544

  alpha__1 0.424819 0.041257 0.001618 0.348102 0.509757 804.760648

  alpha__2 0.456817 0.057200 0.002011 0.321125 0.553173 800.225916

  alpha__3 0.268148 0.044879 0.001725 0.182744 0.352197 724.967532

  alpha__4 0.651465 0.057472 0.002197 0.544076 0.761216 978.073246

  Rhat

  alpha__0 0.998637

  alpha__1 0.999540

  alpha__2 0.998075

  alpha__3 0.998995

  alpha__4 0.998060

  ? 每个时间间隔的汇总统计(仅显示前5个和alpha)。

  图13-22表现的是估算值的一个子集,说明回归参数alpha和beta随时间演变:

  In [33]: sh=np.shape(trace_rw['alpha']) ?

  sh ?

  Out[33]: (500, 42)

  In [34]: part_dates=np.linspace(min(mpl_dates),

  max(mpl_dates), sh[1]) ?

  In [35]: index=[dt.datetime.fromordinal(int(date)) for

  date in part_dates] ?

  In [36]: alpha={'alpha_%i' % i: v for i, v in

  enumerate(trace_rw['alpha']) if i < 20} ?

  In [37]: beta={'beta_%i' % i: v for i, v in

  enumerate(trace_rw['beta']) if i < 20} ?

  In [38]: df_alpha=pd.DataFrame(alpha, index=index) ?

  In [39]: df_beta=pd.DataFrame(beta, index=index) ?

  In [40]: ax=df_alpha.plot(color='b', style='-.', legend=False,

  lw=0.7, figsize=(10, 6))

  df_beta.plot(color='r', style='-.', legend=False,

  lw=0.7, ax=ax)

  plt.ylabel('alpha/beta');

  ? 包含参数估算值的对象构成。

  ? 创建日期列表以匹配时间间隔数量。

  ? 将相关的参数时间序列收集在两个DataFrame对象中。

  

  图13-22 一段时间的选择参数估算值

  绝对价格数据和相对收益率数据

  本节的分析基于规范化价格数据。这仅仅是为了说明目的,因为对应的图形化结果更容易理解和解读(它们在视觉上“更引人注目”)。但是,现实世界中的金融应用应该依赖收益率数据,以确保时间序列数据的稳定性。

  使用alpha和beta均值,我们可以说明一段时间内回归的更新。图 13-23 展示了回归随时间推移而更新的情况。此外,它还显示了从alpha和beta均值得出的39条回归线。很明显,随时间推移而更新数大大提高了回归拟合(对于当前/最新的数据)——换言之,每个时间周期都需要自己的回归:

  In [41]: plt.figure(figsize=(10, 6))

  plt.scatter(data['GDX'], data['GLD'], c=mpl_dates,

  marker='o', cmap='coolwarm')

  plt.colorbar(ticks=mpl.dates.DayLocator(interval=250),

  format=mpl.dates.DateFormatter('%d %b %y'))

  plt.xlabel('GDX')

  plt.ylabel('GLD')

  x=np.linspace(min(data['GDX']), max(data['GDX']))

  for i in range(sh[1]): ?

  alpha_rw=npan(trace_rw['alpha'].T[i])

  beta_rw=npan(trace_rw['beta'].T[i])

  plt.plot(x, alpha_rw + beta_rw * x, '--', lw=0.7,

  color=plt.cmwarm(i / sh[1]))

  ? 为所有长度为50的时间间隔绘制回归线。

  

  图13-23 包含时间相关回归线(更新的估算)的散点图

  关于贝叶斯回归的介绍到此告一段落,Python提供了强大的PyMC3库,以实现贝叶斯统计以及概率编程中的不同方法,尤其是贝叶斯回归,它在计量金融学中已经成为相当流行而重要的工具。

  本文摘自《Python金融大数据分析 第2版》

  

  《Python金融大数据分析 第2版》分为5部分,共21章。第1部分介绍了Python在金融学中的应用,其内容涵盖了Python用于金融行业的原因、Python的基础架构和工具,以及Python在计量金融学中的一些具体入门实例;第2部分介绍了Python的基础知识以及Python中非常有名的库NumPy和pandas工具集,还介绍了面向对象编程;第3部分介绍金融数据科学的相关基本技术和方法,包括数据可视化、输入/输出操作和数学中与金融相关的知识等;第4部分介绍Python在算法交易上的应用,重点介绍常见算法,包括机器学习、深度神经网络等人工智能相关算法;第5部分讲解基于蒙特卡洛模拟开发期权及衍生品定价的应用,其内容涵盖了估值框架的介绍、金融模型的模拟、衍生品的估值、投资组合的估值等知识。

  《Python金融大数据分析 第2版》本书适合对使用Python进行大数据分析、处理感兴趣的金融行业开发人员阅读。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
常见统计分布的概率分布图
【免费赠书】Python如何实现数据可视化?
三维散点图
Matplotlib数学表达式
数量经济学的Python实现(一)|公司库存的动态变化
关于Markov Chain & Monte Carlo
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服