【案例】Python金融分析-CAPM模型对股票进行分析

前言

在2009年巴菲特给股东们的信中写道:

Investors should be skeptical of history-based models. Constructed by a nerdy-sounding priesthood using esoteric terms such as beta, gamma, sigma, and the like, these models tend to look impressive. Too often, though, investors forget to examine the assumptions behind the symbols.

这句话的大概意思就是:投资者应该对基于历史的模型保持怀疑态度。因为它们是由一些书呆子气似的人所定义的,用了一些所谓的β、γ、σ等符号来表示,看起来很高端。但,投资者们通常会忘记审视这些符号背后的条件和假设

本案例名为《基于CAPM的股票风险和预期》,是我去年上金融分析+python课程的时候做的作业,选取了S&P500上的481个股票进行分析,最终得到结论:相比于模型计算的预期,XRX是表现最好的,HBI表现最差;而就企业特定风险(firm specific risk)而言,HON的风险最低,DUK的风险最高


优劣势

CAPM理所当然是基于历史得到的模型,但它也有一定的优劣势:

  • 劣势:
    • 并不完全准确,实际上任何模型都是这样
    • 其基于的假设可能并不现实
  • 优势:
    • 尽管一些其他模型(如APM、多因子模型)可能比CAPM更好,但当涉及到估计未来预期回报时,它们的有效性会下降
    • CAPM模型比其他模型需要更少的复杂的信息
    • 对大多数公司来说,不同模型计算得到的预期回报差别不大

符号术语

在CAPM模型中用到两个符号:β和α

  • β:一项潜在投资的β值是衡量该投资将为投资组合增加多少风险的指标
  • α:其实全名为Jensen’s Alpha,是一个衡量投资组合所获得的超额回报与CAPM模型建议所得的回报相比的指标

股票绩效

分析股票的绩效前,首先得分别预测β和α


预估β值

β正如前所述,是一个给定股票的风险的度量。预估β值的回归公式如下
R j = a + β R m R_j =a+βR_m Rj=a+βRm
其中 R j R_j Rj是股票在历史上的回报, R m R_m Rm是市场的回报,回归后得到的直线的斜率也就是我们要预估的β值

当然,在预估前我们还需要有更细节的东西需要处理:

  1. 决定一个预估期间,如2011到2020这十年
  2. 决定一个回报间隔,如按每天、每周或每月计算我们所需的R值
  3. 计算股票的收益,即 R j = ( P r i c e E n d − P r i c e B e g i n n i n g + D i v i d e n d s P e r i o d ) / P r i c e B e g i n n i n g R_j = (Price_{End} - Price_{Beginning} + Dividends_{Period})/ Price_{Beginning} Rj=(PriceEndPriceBeginning+DividendsPeriod)/PriceBeginning,其中 P r i c e E n d Price_{End} PriceEnd P r i c e B e g i n n i n g Price_{Beginning} PriceBeginning为回报间隔内最终的股价和开始的股价, D i v i d e n d s P e r i o d Dividends_{Period} DividendsPeriod仅在除息月才加入计算
  4. 选择一个市场指数,比如道琼斯、S&P500

有了以上的步骤就能计算出我们所需的β值了


预估α值

预估α前对CAPM进行变式如下:
R j = R f + β ( R m − R f ) = R f ( 1 − β ) + β R m R_j = R_f + β (R_m - R_f) = R_f (1-β) + β R_m Rj=Rf+β(RmRf)=Rf(1β)+βRm
其中, R f R_f Rf为Risk free值,需要进行额外的计算,在后面代码部分会展示;在上式中,我们将 R f ( 1 − β ) R_f (1-β) Rf(1β)类比为预估β值公式中的 a a a,这样一来, a − R f ( 1 − β ) a-R_f (1-β) aRf(1β)就是我们需要的Jensen’s Alpha,当 a > R f ( 1 − β ) a > R_f (1-β) a>Rf(1β),意味着股票在回归区间的表现比预期的要好


分析绩效

基于以上指标,现给出整个流程的具体计算步骤:

  1. 计算一只股票在5年内的每月回报率
  2. 计算5年估计期内S&P500指数的月回报率
  3. 对这些收益做线性回归
  4. 得到截距a和斜率β
  5. 比较截距a和 R f ( 1 − β ) R_f (1-β) Rf(1β)
  6. 给出结论

评估

在给出结论前,当然需要进行评估,本案例中采用 R 2 R^2 R2 1 − R 2 1-R^2 1R2进行评估,这是在回归中能够得到的对回归的评估数值。这里回归的 R 2 R^2 R2是一个公司的风险估计比例,归因于市场风险(即外在风险);而 1 − R 2 1-R^2 1R2是归因于企业特定风险(firm specific risk),即内在风险;当你是一个多样化的投资者,你可能倾向于在意外在风险,当你是单一化的投资者,你可能倾向于选择firm specific risk的公司进行投资


代码解读

获取数据

下载S&P500上的股票,利用alphavantage的api来获取;输入中的symbol为股票的名称,apikey需要自己去alphavantage的官网申请

def getMonthlyStockPrices(symbol, apikey):ts = TimeSeries( key=apikey )data, meta_data = ts.get_monthly_adjusted( symbol ) # 获取数据symbol_df = pd.DataFrame.from_dict( data, orient = 'index' ) # 转化为DataFramesymbol_df = symbol_df.apply(pd.to_numeric) # 数字化symbol_df.index = pd.to_datetime( symbol_df.index ) # 换indexsymbol_df.columns = [ 'open', 'high', 'low', 'close', 'adjusted_close', 'volume', 'dividend_amt'] # 给定对应列的名称symbol_df = symbol_df.sort_index( ascending=True ) # 排序return symbol_df

得到的数据如下
在这里插入图片描述


计算rick free值

tbill_data = pd.read_excel('../../General Resources/tbilldata.xlsx', skiprows=1) # skiprows=1 跳过第一行
tbill_data.index = pd.to_datetime(tbill_data['DATE'])                            # DATE column 转化成 index
tbill_data = tbill_data.drop('DATE', axis=1)                                     # 再删去DATE这个column
_15_19_byYear = tbill_data.loc['2015':'2019']['BANK DISCOUNT.2'] # 获取固定区间数据
Sum = []
for year in range(2015,2020):Sum.append((_15_19_byYear.loc[str(year)]/100).mean()) 
avg_annual = np.mean(Sum)
print("avg_annual", avg_annual)
Monthly_risk_free = avg_annual/12
print("Monthly_risk_free", Monthly_risk_free)

于是得到输出为

avg_annual 0.010698813575433277
Monthly_risk_free 0.0008915677979527731

计算β、α值

# 得到β值
def getInterceptSlope(stock_ret, index_ret):model_data = pd.concat([stock_ret, index_ret], axis=1)model_data.columns = ['stock_ret', 'index_ret']# fit the data with ols modelresults = smf.ols('stock_ret ~ index_ret', data=model_data).fit()intercept = results.params.Interceptslope = results.params.index_retrsquared = results.rsquaredreturn intercept, slope, rsquared
# 得到Rf(1-β)的值
def getRiskfree_1_beta(Rf, slope_name, Slopes):return Rf*(1-Slopes[slope_name])
# 得到单个α值
def getSingleAlpha(Intercepts,Rf_1_beta):return Intercepts-Rf_1_beta
# 得到所有alpha值
def getAlphas(stocks, index, Rf):Intercepts = {}Slopes = {}rsquareds = {}index_ret = ((index.close.diff() + index.dividend_amt.shift())/index.close.shift()).bfill()for stock in stocks:stock_ret = ((stocks[stock].close.diff() + stocks[stock].dividend_amt.shift())/stocks[stock].close.shift()).bfill()I_S = getInterceptSlope(stock_ret, index_ret)Intercepts[stock] = I_S[0]Slopes[stock] = I_S[1]rsquareds[stock] = I_S[2]Rf_1_beta = {}for slope_name in Slopes:Rf_1_beta[slope_name] = getRiskfree_1_beta(Rf, slope_name, Slopes)Alphas = {}for name in Intercepts:Alphas[name] = getSingleAlpha(Intercepts[name],Rf_1_beta[name])return Alphas, rsquareds
Alphas, rsquareds = getAlphas(Stocks, GSPC_index, Monthly_risk_free)

得到α和 R 2 R^2 R2的最大最小值

max_alpha = -1
max_alpha_name = 0
min_alpha = 1
min_alpha_name = 0
for alpha in Alphas:if(Alphas[alpha]>max_alpha):max_alpha = Alphas[alpha]max_alpha_name = alphaif(Alphas[alpha]<min_alpha):min_alpha = Alphas[alpha]min_alpha_name = alpha
print("min_alpha:", min_alpha_name, ":", min_alpha, "annual excess return:", (1+min_alpha)**12-1)
print("max_alpha:", max_alpha_name, ":", max_alpha, "annual excess return:", (1+max_alpha)**12-1)max_rsquared = -1
max_rsquared_name = 0
min_rsquared = 1
min_rsquared_name = 0
for rsquared in rsquareds:if(rsquareds[rsquared]>max_rsquared):max_rsquared = rsquareds[rsquared]max_rsquared_name = rsquaredif(rsquareds[rsquared]<min_rsquared):min_rsquared = rsquareds[rsquared]min_rsquared_name = rsquared
print("min_rsquared:",min_rsquared_name, ":", min_rsquared)
print("max_rsquared:",max_rsquared_name, ":", max_rsquared)

得到输出为

min_alpha: HBI : -0.027197615160351105 annual excess return: -0.2817171133835983
max_alpha: XRX : 0.04446442315931073 annual excess return: 0.6854808327225679
min_rsquared: DUK : 1.5945826779772965e-05
max_rsquared: HON : 0.6987161635840791

可视化

fig1, ax = plt.subplots(2, 2, figsize=(14,14))
index_ret = (GSPC_index.close - GSPC_index.open + GSPC_index.dividend_amt)/GSPC_index.open# stock with max_alpha
stock_ret = ((Stocks[max_alpha_name].close.diff() + Stocks[max_alpha_name].dividend_amt.shift()\)/Stocks[max_alpha_name].close.shift()).bfill()
ISR = getInterceptSlope(stock_ret, index_ret)
ax[0,0].scatter(index_ret, stock_ret)
ax[0,0].plot(index_ret, ISR[0]+ISR[1]*index_ret, "r")
ax[0,0].title.set_text("Return on "+max_alpha_name+"={:.4f}+{:.4f}*Return on Market".format(ISR[0],ISR[1])\+"  α(max): {:.4f}".format(max_alpha))# stock with min_alpha
stock_ret = ((Stocks[min_alpha_name].close.diff() + Stocks[min_alpha_name].dividend_amt.shift()\)/Stocks[min_alpha_name].close.shift()).bfill()
ISR = getInterceptSlope(stock_ret, index_ret)
ax[0,1].scatter(index_ret, stock_ret)
ax[0,1].plot(index_ret, ISR[0]+ISR[1]*index_ret, "r")
ax[0,1].title.set_text("Return on "+min_alpha_name+"={:.4f}+{:.4f}*Return on Market".format(ISR[0],ISR[1])\+"  α(min): {:.4f}".format(min_alpha))# stock with max_rsquared
stock_ret = ((Stocks[max_rsquared_name].close.diff() + Stocks[max_rsquared_name].dividend_amt.shift()\)/Stocks[max_rsquared_name].close.shift()).bfill()
ISR = getInterceptSlope(stock_ret, index_ret)
ax[1,0].scatter(index_ret, stock_ret)
ax[1,0].plot(index_ret, ISR[0]+ISR[1]*index_ret, "r")
ax[1,0].title.set_text("Return on "+max_rsquared_name+"={:.4f}+{:.4f}*Return on Market".format(ISR[0],ISR[1])\+"  R²(max): {:.4f}".format(max_rsquared))# stock with min_rsquared
stock_ret = ((Stocks[min_rsquared_name].close.diff() + Stocks[min_rsquared_name].dividend_amt.shift()\)/Stocks[min_rsquared_name].close.shift()).bfill()
ISR = getInterceptSlope(stock_ret, index_ret)
ax[1,1].scatter(index_ret, stock_ret)
ax[1,1].plot(index_ret, ISR[0]+ISR[1]*index_ret, "r")
ax[1,1].title.set_text("Return on "+min_rsquared_name+"={:.4f}+{:.4f}*Return on Market".format(ISR[0],ISR[1])\+"  R²(min): {:.4f}".format(min_rsquared))plt.show()

得到可视化如下

在这里插入图片描述
由图,最终得到结论:相比于模型计算的预期,XRX是表现最好的,HBI表现最差;而就企业特定风险而言,HON的风险最低,DUK的风险最高

源码获取

完整源码和文件见此处

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/64940.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软 Build 2023:人工智能重新定义软件开发与工作的未来

2023年5月23日&#xff0c;美国华盛顿州西雅图 —— 微软年度开发者大会Build 2023在西雅图开幕&#xff0c;面对当今由AI引领的技术趋势&#xff0c;微软向超过20万名注册参会的开发者集中展示人工智能如何给软件开发的对象、过程和工具带来巨大变革&#xff0c;并重新定义工作…

chatgpt赋能python:Python到底要怎么删干净?

Python 到底要怎么删干净&#xff1f; 介绍 Python 是一门广泛使用的编程语言。由于其简单易学、可扩展和跨平台特性&#xff0c;目前越来越多的程序员和公司开始在其项目中采用 Python。然而&#xff0c;由于初学者较多&#xff0c;可能会遇到 Python 删干净的问题&#xff…

chatgpt赋能python:Python怎么删除库

Python怎么删除库 如果你有一些不再需要的Python库&#xff0c;可能会想要将它们删除以释放硬盘空间或简化项目环境。那么&#xff0c;Python怎么删除库呢&#xff1f; 一级标题&#xff1a;使用pip命令卸载库 Python语言中&#xff0c;有一个常用的工具pip&#xff0c;它是…

chatgpt赋能python:Python彻底删除指南:保障您的数据不被误操作删除

Python彻底删除指南&#xff1a;保障您的数据不被误操作删除 Python是一种简单易学且功能强大的编程语言&#xff0c;因此被广泛应用于各种应用程序的开发和数据分析。然而&#xff0c;当我们在使用Python时&#xff0c;难免会遇到删除数据的情况。 误操作删除数据可谓是一场…

chatgpt赋能python:Python如何删除数据:完整指南

Python如何删除数据&#xff1a;完整指南 如果你是一名Python开发者&#xff0c;你肯定需要删除不必要的数据。删除数据是很重要的&#xff0c;因为它可以帮助你保持你的数据库或文件系统的清洁和可读性。本文将详细介绍Python中删除数据的各种方法&#xff0c;让你轻松地管理…

chatgpt赋能python:Python彻底删除指南:为什么需要彻底删除Python?

Python彻底删除指南&#xff1a;为什么需要彻底删除Python&#xff1f; Python是一种高级编程语言&#xff0c;它广泛用于各种应用程序的开发。但是&#xff0c;对于某些开发者来说&#xff0c;可能需要从他们的计算机中彻底删除Python。这可能是因为他们需要将Python版本更新…

chatgpt赋能python:如何彻底删除Python软件?

如何彻底删除Python软件&#xff1f; Python是一种非常流行的编程语言&#xff0c;经常被用于开发应用程序、网站、数据分析和机器学习等。但是&#xff0c;有时候你可能需要删除Python软件&#xff0c;可能是因为你需要卸载旧版本的Python&#xff0c;或者因为你需要清理磁盘…

chatgpt赋能python:Python如何彻底删除软件

Python如何彻底删除软件 Python是一种非常流行的高级编程语言&#xff0c;可用于构建各种不同类型的软件和网络应用程序。但是&#xff0c;在某些情况下&#xff0c;我们可能需要卸载不再需要的软件&#xff0c;并希望能够从系统中完全删除软件。在本文中&#xff0c;我们将探…

chatgpt赋能python:如何彻底删除Python?

如何彻底删除Python&#xff1f; Python是一种非常流行的编程语言&#xff0c;它被Python开发者&#xff0c;学生和数据科学家广泛使用。但是你可能想要删除它&#xff0c;因为你需要释放空间或者升级到新版本。在本文中&#xff0c;我将向您介绍如何彻底删除Python。 为什么…

chatgpt赋能python:如何彻底删除Python

如何彻底删除Python Python是一种流行的编程语言&#xff0c;但是有时候你可能需要彻底删除它。本文将介绍如何删除Python&#xff0c;包括清理Python相关文件和文件夹&#xff0c;以便你可以完全卸载它。 确认Python版本 在删除Python之前&#xff0c;需要先确认你当前的Py…

chatgpt赋能python:Python彻底删除教程

Python 彻底删除教程 Python作为一门高级编程语言&#xff0c;广泛应用于人工智能、数据科学、网站开发和自动化等领域。然而&#xff0c;随着工程和项目的不断迭代&#xff0c;代码的删除也变得越来越重要。在本教程中&#xff0c;我们将重点介绍如何彻底删除Python代码&…

chatgpt赋能python:如何彻底删掉Python

如何彻底删掉Python 介绍 Python是一种流行的编程语言&#xff0c;但有时候你可能需要完全删除Python&#xff0c;比如你想节省硬盘空间或需要卸载旧版本并安装新版本。本文将介绍如何彻底删除Python。 删除Python Windows上的Python卸载 在Windows上&#xff0c;你可以通…

Qt设计师的简单使用(ui设计界面的简单使用)

文章目录 一、界面的基本介绍二、添加控件2.1 添加控件2.2 设置控件属性 三、布局器的使用3.1 布局器介绍3.2 简单布局 3.3 复杂布局3.4 带分裂器的布局 四、拓展4.1 添加模块窗口4.2 转到槽的使用4.3 拓展 总结 一、界面的基本介绍 下方为Qt设计师界面基本内容&#xff08;因…

重塑未来:AI对教育行业的深远影响与挑战

自从AI人工智能的发展进入“iPhone时刻”以来&#xff0c;我们已身处一个日新月异的时代。在众多领域&#xff0c;AI已经大放异彩&#xff0c;而教育作为培养下一代的关键领域&#xff0c;自然也受到了这场科技革命的影响。 AI对教育行业重大影响 最近可汗学院&#xff08;Kh…

ChatGPT:重塑交流方式的重要性和影响|小智AI

ChatGPT丨小智ai丨chatgpt丨人工智能丨OpenAI丨聊天机器人丨AI语音助手丨GPT-3.5丨OpenAI ChatGPT|GPT-4|GPT-3|人机对话|ChatGPT应用|小智ai|小智ai|小智ai|小智ai|小智AI|chatgpt小智AI 导言&#xff1a; 人工智能技术的快速发展已经带来了许多令人惊叹的突破&#xff0c;其…

MLC Chat App上架苹果App Store可在本地运行语言模型;谷歌推出AI设计工具StyleDrop

&#x1f989; AI新闻 &#x1f680; 开源项目MLC Chat App上架苹果App Store&#xff0c;可在本地运行语言模型 摘要&#xff1a;CMU助理陈天其教授表示&#xff0c;开源项目MLC LLM的独立聊天应用程序MLC Chat App已上架苹果App Store&#xff0c;允许将任何语言模型本地部…

苹果开发 笔记(94)

已经很久没写苹果开发相关的&#xff0c;真有点忘记了ios 是如何写。或许久了连一个界面都不知道如何写。 在我回忆里面其实并没有大规模上架应用或者面试过程用到&#xff0c;的确在接触ios和苹果这块&#xff0c;进程通信&#xff0c;线程处理&#xff0c;事件&#xff0c;网…

SEO技术风口来了|SEO能否抓住全球约93%的网络用户?

开篇词 作者/出品人 | 美洽 SEO 流量专家 白桦 为什么要做一个 SEO 专栏&#xff1f; 在一部分人眼中&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;已经是老掉牙的玩意儿&#xff0c;在这个信息爆炸的年代&#xff0c;它似乎已经无法承担吸引流量的主要作用。 但&…

最新 Google支付 Google Play 结算库 4.0 版:从创建定价、商品到测试、支付成功等步骤

使用 Google Play 结算系统&#xff0c;分为线上gp后台配置和代码billing集成&#xff0c;以下都以应用内产品为例。我做的是小说&#xff0c;应用内购买的是书币。 后台配置&#xff1a;前提能科学上网 设定定价&#xff0c;就是商品的定价&#xff1a; 按图所示建立价格&am…

Android 接入Google应用订阅与应用内支付结算笔记

公司项目是在谷歌应用商店上线发布的&#xff0c;最近产品经理说要给项目加个订阅的功能&#xff0c;按月订阅免广告的形式&#xff0c;对于我来说也是第一次接入谷歌应用商店的订阅&#xff0c;支付功能&#xff0c;是照着谷歌的官方文档集成边测试才做出的&#xff0c;下面分…