siat 3.10.132__py3-none-any.whl → 3.11.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- siat/__init__.py +0 -0
- siat/allin.py +8 -0
- siat/assets_liquidity.py +0 -0
- siat/beta_adjustment.py +0 -0
- siat/beta_adjustment_china.py +0 -0
- siat/blockchain.py +0 -0
- siat/bond.py +0 -0
- siat/bond_base.py +0 -0
- siat/bond_china.py +0 -0
- siat/bond_zh_sina.py +0 -0
- siat/capm_beta.py +0 -0
- siat/capm_beta2.py +4 -4
- siat/common.py +9 -6
- siat/compare_cross.py +0 -0
- siat/copyrights.py +0 -0
- siat/cryptocurrency.py +0 -0
- siat/economy.py +0 -0
- siat/economy2.py +0 -0
- siat/esg.py +0 -0
- siat/event_study.py +0 -0
- siat/exchange_bond_china.pickle +0 -0
- siat/fama_french.py +0 -0
- siat/fin_stmt2_yahoo.py +0 -0
- siat/financial_base.py +0 -0
- siat/financial_statements.py +0 -0
- siat/financials.py +0 -0
- siat/financials2.py +0 -0
- siat/financials_china.py +0 -0
- siat/financials_china2.py +0 -0
- siat/fund.py +0 -0
- siat/fund_china.pickle +0 -0
- siat/fund_china.py +0 -0
- siat/future_china.py +0 -0
- siat/google_authenticator.py +0 -0
- siat/grafix.py +55 -4
- siat/holding_risk.py +0 -0
- siat/luchy_draw.py +0 -0
- siat/market_china.py +0 -0
- siat/markowitz.py +0 -0
- siat/markowitz2.py +1 -0
- siat/markowitz2_20250704.py +0 -0
- siat/markowitz2_20250705.py +0 -0
- siat/markowitz_simple.py +0 -0
- siat/ml_cases.py +0 -0
- siat/ml_cases_example.py +0 -0
- siat/option_china.py +0 -0
- siat/option_pricing.py +0 -0
- siat/other_indexes.py +0 -0
- siat/risk_adjusted_return.py +0 -0
- siat/risk_adjusted_return2.py +8 -4
- siat/risk_evaluation.py +0 -0
- siat/risk_free_rate.py +0 -0
- siat/save2docx.py +345 -0
- siat/save2pdf.py +145 -0
- siat/sector_china.py +0 -0
- siat/security_price2.py +0 -0
- siat/security_prices.py +168 -6
- siat/security_trend.py +0 -0
- siat/security_trend2.py +2 -2
- siat/stock.py +11 -1
- siat/stock_advice_linear.py +0 -0
- siat/stock_base.py +0 -0
- siat/stock_china.py +0 -0
- siat/stock_info.pickle +0 -0
- siat/stock_prices_kneighbors.py +0 -0
- siat/stock_prices_linear.py +0 -0
- siat/stock_profile.py +0 -0
- siat/stock_technical.py +0 -0
- siat/stooq.py +0 -0
- siat/transaction.py +0 -0
- siat/translate.py +0 -0
- siat/valuation.py +0 -0
- siat/valuation_china.py +0 -0
- siat/var_model_validation.py +0 -0
- siat/yf_name.py +0 -0
- {siat-3.10.132.dist-info/licenses → siat-3.11.1.dist-info}/LICENSE +0 -0
- {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/METADATA +234 -235
- siat-3.11.1.dist-info/RECORD +80 -0
- {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/WHEEL +1 -1
- {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/top_level.txt +0 -1
- build/lib/build/lib/siat/__init__.py +0 -75
- build/lib/build/lib/siat/allin.py +0 -137
- build/lib/build/lib/siat/assets_liquidity.py +0 -915
- build/lib/build/lib/siat/beta_adjustment.py +0 -1058
- build/lib/build/lib/siat/beta_adjustment_china.py +0 -548
- build/lib/build/lib/siat/blockchain.py +0 -143
- build/lib/build/lib/siat/bond.py +0 -2900
- build/lib/build/lib/siat/bond_base.py +0 -992
- build/lib/build/lib/siat/bond_china.py +0 -100
- build/lib/build/lib/siat/bond_zh_sina.py +0 -143
- build/lib/build/lib/siat/capm_beta.py +0 -783
- build/lib/build/lib/siat/capm_beta2.py +0 -887
- build/lib/build/lib/siat/common.py +0 -5360
- build/lib/build/lib/siat/compare_cross.py +0 -642
- build/lib/build/lib/siat/copyrights.py +0 -18
- build/lib/build/lib/siat/cryptocurrency.py +0 -667
- build/lib/build/lib/siat/economy.py +0 -1471
- build/lib/build/lib/siat/economy2.py +0 -1853
- build/lib/build/lib/siat/esg.py +0 -536
- build/lib/build/lib/siat/event_study.py +0 -815
- build/lib/build/lib/siat/fama_french.py +0 -1521
- build/lib/build/lib/siat/fin_stmt2_yahoo.py +0 -982
- build/lib/build/lib/siat/financial_base.py +0 -1160
- build/lib/build/lib/siat/financial_statements.py +0 -598
- build/lib/build/lib/siat/financials.py +0 -2339
- build/lib/build/lib/siat/financials2.py +0 -1278
- build/lib/build/lib/siat/financials_china.py +0 -4433
- build/lib/build/lib/siat/financials_china2.py +0 -2212
- build/lib/build/lib/siat/fund.py +0 -629
- build/lib/build/lib/siat/fund_china.py +0 -3307
- build/lib/build/lib/siat/future_china.py +0 -551
- build/lib/build/lib/siat/google_authenticator.py +0 -47
- build/lib/build/lib/siat/grafix.py +0 -3636
- build/lib/build/lib/siat/holding_risk.py +0 -867
- build/lib/build/lib/siat/luchy_draw.py +0 -638
- build/lib/build/lib/siat/market_china.py +0 -1168
- build/lib/build/lib/siat/markowitz.py +0 -2363
- build/lib/build/lib/siat/markowitz2.py +0 -3150
- build/lib/build/lib/siat/markowitz2_20250704.py +0 -2969
- build/lib/build/lib/siat/markowitz2_20250705.py +0 -3158
- build/lib/build/lib/siat/markowitz_simple.py +0 -373
- build/lib/build/lib/siat/ml_cases.py +0 -2291
- build/lib/build/lib/siat/ml_cases_example.py +0 -60
- build/lib/build/lib/siat/option_china.py +0 -3069
- build/lib/build/lib/siat/option_pricing.py +0 -1925
- build/lib/build/lib/siat/other_indexes.py +0 -409
- build/lib/build/lib/siat/risk_adjusted_return.py +0 -1576
- build/lib/build/lib/siat/risk_adjusted_return2.py +0 -1900
- build/lib/build/lib/siat/risk_evaluation.py +0 -2218
- build/lib/build/lib/siat/risk_free_rate.py +0 -351
- build/lib/build/lib/siat/sector_china.py +0 -4140
- build/lib/build/lib/siat/security_price2.py +0 -727
- build/lib/build/lib/siat/security_prices.py +0 -3408
- build/lib/build/lib/siat/security_trend.py +0 -402
- build/lib/build/lib/siat/security_trend2.py +0 -646
- build/lib/build/lib/siat/stock.py +0 -4284
- build/lib/build/lib/siat/stock_advice_linear.py +0 -934
- build/lib/build/lib/siat/stock_base.py +0 -26
- build/lib/build/lib/siat/stock_china.py +0 -2095
- build/lib/build/lib/siat/stock_prices_kneighbors.py +0 -910
- build/lib/build/lib/siat/stock_prices_linear.py +0 -386
- build/lib/build/lib/siat/stock_profile.py +0 -707
- build/lib/build/lib/siat/stock_technical.py +0 -3305
- build/lib/build/lib/siat/stooq.py +0 -74
- build/lib/build/lib/siat/transaction.py +0 -347
- build/lib/build/lib/siat/translate.py +0 -5183
- build/lib/build/lib/siat/valuation.py +0 -1378
- build/lib/build/lib/siat/valuation_china.py +0 -2076
- build/lib/build/lib/siat/var_model_validation.py +0 -444
- build/lib/build/lib/siat/yf_name.py +0 -811
- build/lib/siat/__init__.py +0 -75
- build/lib/siat/allin.py +0 -137
- build/lib/siat/assets_liquidity.py +0 -915
- build/lib/siat/beta_adjustment.py +0 -1058
- build/lib/siat/beta_adjustment_china.py +0 -548
- build/lib/siat/blockchain.py +0 -143
- build/lib/siat/bond.py +0 -2900
- build/lib/siat/bond_base.py +0 -992
- build/lib/siat/bond_china.py +0 -100
- build/lib/siat/bond_zh_sina.py +0 -143
- build/lib/siat/capm_beta.py +0 -783
- build/lib/siat/capm_beta2.py +0 -887
- build/lib/siat/common.py +0 -5360
- build/lib/siat/compare_cross.py +0 -642
- build/lib/siat/copyrights.py +0 -18
- build/lib/siat/cryptocurrency.py +0 -667
- build/lib/siat/economy.py +0 -1471
- build/lib/siat/economy2.py +0 -1853
- build/lib/siat/esg.py +0 -536
- build/lib/siat/event_study.py +0 -815
- build/lib/siat/fama_french.py +0 -1521
- build/lib/siat/fin_stmt2_yahoo.py +0 -982
- build/lib/siat/financial_base.py +0 -1160
- build/lib/siat/financial_statements.py +0 -598
- build/lib/siat/financials.py +0 -2339
- build/lib/siat/financials2.py +0 -1278
- build/lib/siat/financials_china.py +0 -4433
- build/lib/siat/financials_china2.py +0 -2212
- build/lib/siat/fund.py +0 -629
- build/lib/siat/fund_china.py +0 -3307
- build/lib/siat/future_china.py +0 -551
- build/lib/siat/google_authenticator.py +0 -47
- build/lib/siat/grafix.py +0 -3636
- build/lib/siat/holding_risk.py +0 -867
- build/lib/siat/luchy_draw.py +0 -638
- build/lib/siat/market_china.py +0 -1168
- build/lib/siat/markowitz.py +0 -2363
- build/lib/siat/markowitz2.py +0 -3150
- build/lib/siat/markowitz2_20250704.py +0 -2969
- build/lib/siat/markowitz2_20250705.py +0 -3158
- build/lib/siat/markowitz_simple.py +0 -373
- build/lib/siat/ml_cases.py +0 -2291
- build/lib/siat/ml_cases_example.py +0 -60
- build/lib/siat/option_china.py +0 -3069
- build/lib/siat/option_pricing.py +0 -1925
- build/lib/siat/other_indexes.py +0 -409
- build/lib/siat/risk_adjusted_return.py +0 -1576
- build/lib/siat/risk_adjusted_return2.py +0 -1900
- build/lib/siat/risk_evaluation.py +0 -2218
- build/lib/siat/risk_free_rate.py +0 -351
- build/lib/siat/sector_china.py +0 -4140
- build/lib/siat/security_price2.py +0 -727
- build/lib/siat/security_prices.py +0 -3408
- build/lib/siat/security_trend.py +0 -402
- build/lib/siat/security_trend2.py +0 -646
- build/lib/siat/stock.py +0 -4284
- build/lib/siat/stock_advice_linear.py +0 -934
- build/lib/siat/stock_base.py +0 -26
- build/lib/siat/stock_china.py +0 -2095
- build/lib/siat/stock_prices_kneighbors.py +0 -910
- build/lib/siat/stock_prices_linear.py +0 -386
- build/lib/siat/stock_profile.py +0 -707
- build/lib/siat/stock_technical.py +0 -3305
- build/lib/siat/stooq.py +0 -74
- build/lib/siat/transaction.py +0 -347
- build/lib/siat/translate.py +0 -5183
- build/lib/siat/valuation.py +0 -1378
- build/lib/siat/valuation_china.py +0 -2076
- build/lib/siat/var_model_validation.py +0 -444
- build/lib/siat/yf_name.py +0 -811
- siat-3.10.132.dist-info/RECORD +0 -218
@@ -1,1058 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
"""
|
4
|
-
版权:王德宏,北京外国语大学国际商学院
|
5
|
-
功能:计算CAPM模型贝塔系数的调整值
|
6
|
-
版本:2.1,2019-7-25
|
7
|
-
"""
|
8
|
-
|
9
|
-
#==============================================================================
|
10
|
-
#关闭所有警告
|
11
|
-
import warnings; warnings.filterwarnings('ignore')
|
12
|
-
from siat.common import *
|
13
|
-
from siat.translate import *
|
14
|
-
from siat.grafix import *
|
15
|
-
from siat.security_prices import *
|
16
|
-
from siat.security_price2 import *
|
17
|
-
#==============================================================================
|
18
|
-
import matplotlib.pyplot as plt
|
19
|
-
|
20
|
-
#处理绘图汉字乱码问题
|
21
|
-
import sys; czxt=sys.platform
|
22
|
-
if czxt in ['win32','win64']:
|
23
|
-
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
|
24
|
-
mpfrc={'font.family': 'SimHei'}
|
25
|
-
|
26
|
-
if czxt in ['darwin']: #MacOSX
|
27
|
-
plt.rcParams['font.family']= ['Heiti TC']
|
28
|
-
mpfrc={'font.family': 'Heiti TC'}
|
29
|
-
|
30
|
-
if czxt in ['linux']: #website Jupyter
|
31
|
-
plt.rcParams['font.family']= ['Heiti TC']
|
32
|
-
mpfrc={'font.family':'Heiti TC'}
|
33
|
-
|
34
|
-
# 解决保存图像时'-'显示为方块的问题
|
35
|
-
plt.rcParams['axes.unicode_minus'] = False
|
36
|
-
#==============================================================================
|
37
|
-
def prepare_capm(stkcd,mktidx,start,end):
|
38
|
-
"""
|
39
|
-
函数功能:准备计算一只股票CAPM模型贝塔系数的数据,并标记年度
|
40
|
-
输入参数:
|
41
|
-
stkcd: 股票代码
|
42
|
-
mktidx: 指数代码
|
43
|
-
start:使用股票价格数据的开始日期,MM/DD/YYYY
|
44
|
-
end:使用股票价格数据的结束日期,MM/DD/YYYY
|
45
|
-
输出数据:
|
46
|
-
返回数据:带年度标记的可直接用于capm回归的股票收益率数据
|
47
|
-
"""
|
48
|
-
|
49
|
-
#仅用于调试,正式使用前应注释掉
|
50
|
-
#stkcd='002504.SZ'; mktidx='000001.SS'
|
51
|
-
#start="12/31/2011"; end="12/31/2018"
|
52
|
-
|
53
|
-
#抓取股价和指数
|
54
|
-
stock=get_price(stkcd,start,end)
|
55
|
-
if stock is None:
|
56
|
-
print(" #Error(prepare_capm): no data retrieved from server!")
|
57
|
-
return None
|
58
|
-
market=get_price(mktidx,start,end)
|
59
|
-
if market is None:
|
60
|
-
print(" #Error(prepare_capm): no index data retrieved from server!")
|
61
|
-
return None
|
62
|
-
|
63
|
-
#计算日收益率
|
64
|
-
import pandas as pd
|
65
|
-
stkret=pd.DataFrame(stock['Close'].pct_change())
|
66
|
-
mktret=pd.DataFrame(market['Close'].pct_change())
|
67
|
-
|
68
|
-
#合并,去掉空缺
|
69
|
-
R=pd.merge(mktret,stkret,how='left',left_index=True,right_index=True)
|
70
|
-
R=R.dropna()
|
71
|
-
|
72
|
-
#标记各个年度
|
73
|
-
R['Year']=R.index.strftime("%Y")
|
74
|
-
|
75
|
-
#返回带年份的股票收益率序列
|
76
|
-
return R
|
77
|
-
|
78
|
-
if __name__=='__main__':
|
79
|
-
R1=prepare_capm('0700.HK','^HSI','2014-01-01','2018-12-31')
|
80
|
-
|
81
|
-
#==============================================================================
|
82
|
-
#==============================================================================
|
83
|
-
def get_beta_ML(stkcd,mktidx,yearlist,printout=True,graph=True):
|
84
|
-
"""
|
85
|
-
函数功能:使用ML方法调整一只股票的CAPM模型贝塔系数
|
86
|
-
输入参数:
|
87
|
-
stkcd: 股票代码
|
88
|
-
mktidx: 指数代码
|
89
|
-
yearlist:年度列表,列出其中期间的贝塔系数
|
90
|
-
输出数据:
|
91
|
-
显示CAPM市场模型回归的beta, 以及ML调整后的beta系数
|
92
|
-
返回数据:年度CAPM贝塔系数和ML调整后的beta系数
|
93
|
-
"""
|
94
|
-
|
95
|
-
#仅为测试用,完成后应立即注释掉
|
96
|
-
#stkcd='0700.HK'
|
97
|
-
#mktidx='^HSI'
|
98
|
-
#yearlist=['2015','2016','2017','2018']
|
99
|
-
|
100
|
-
Y4=str(int(yearlist[0])-1)
|
101
|
-
start=Y4+'-01-01'
|
102
|
-
end=yearlist[-1]+'-12-31'
|
103
|
-
|
104
|
-
#读取股价并准备好收益率数据
|
105
|
-
try:
|
106
|
-
R=prepare_capm(stkcd,mktidx,start,end)
|
107
|
-
except:
|
108
|
-
print(" #Error(get_beta_ML): Preparing CAPM data failed!")
|
109
|
-
return None
|
110
|
-
|
111
|
-
if (R is None):
|
112
|
-
print(" #Error(get_beta_ML): server time out")
|
113
|
-
return None
|
114
|
-
if (len(R) == 0):
|
115
|
-
print(" #Error(get_beta_ML): server returned empty data")
|
116
|
-
return None
|
117
|
-
|
118
|
-
#用于保存beta(CAPM)和beta(ML)
|
119
|
-
import pandas as pd
|
120
|
-
betas=pd.DataFrame(columns=('Year','Beta(CAPM)','Beta(ML)'))
|
121
|
-
|
122
|
-
#计算Merrill-Lynch方法贝塔系数调整
|
123
|
-
from scipy import stats
|
124
|
-
for year in yearlist:
|
125
|
-
r=R[R['Year']==year]
|
126
|
-
if len(r) != 0:
|
127
|
-
output=stats.linregress(r['Close_x'],r['Close_y'])
|
128
|
-
(beta,alpha,r_value,p_value,std_err)=output
|
129
|
-
beta_ML=beta*2.0/3.0+1.0/3.0
|
130
|
-
#整齐输出
|
131
|
-
#print(year,"%6.4f "%(beta),"%6.4f "%(beta_ML))
|
132
|
-
|
133
|
-
row=pd.Series({'Year':year,'Beta(CAPM)':beta,'Beta(ML)':beta_ML})
|
134
|
-
try:
|
135
|
-
betas=betas.append(row,ignore_index=True)
|
136
|
-
except:
|
137
|
-
betas=betas._append(row,ignore_index=True)
|
138
|
-
|
139
|
-
betas.set_index(["Year"], inplace=True)
|
140
|
-
|
141
|
-
if printout == True: printdf_betas(betas,2)
|
142
|
-
if graph == True:
|
143
|
-
model="贝塔系数的简单调整法"
|
144
|
-
draw2_betas(model,mktidx,stkcd,betas)
|
145
|
-
|
146
|
-
return betas
|
147
|
-
|
148
|
-
#==============================================================================
|
149
|
-
def printdf_betas(df,decimal=2):
|
150
|
-
"""
|
151
|
-
功能:整齐地显示数据框的内容,自动调整各列宽度
|
152
|
-
"""
|
153
|
-
#打印时保留的小数点位数
|
154
|
-
dec="%."+str(decimal)+"f"
|
155
|
-
format=lambda x: dec % x
|
156
|
-
df1=df.applymap(format)
|
157
|
-
|
158
|
-
import pandas as pd
|
159
|
-
#调整最佳列宽
|
160
|
-
old_width = pd.get_option('display.max_colwidth')
|
161
|
-
pd.set_option('display.max_colwidth', -1)
|
162
|
-
print(df1)
|
163
|
-
pd.set_option('display.max_colwidth', old_width)
|
164
|
-
|
165
|
-
return
|
166
|
-
|
167
|
-
if __name__=='__main__':
|
168
|
-
yearlist=gen_yearlist['2010','2019']
|
169
|
-
betas=get_beta_ML('AAPL','^GSPC',yearlist)
|
170
|
-
betas2=get_beta_ML('BILI','^GSPC',yearlist)
|
171
|
-
betas3=get_beta_ML('0700.HK','^HSI',yearlist)
|
172
|
-
yearlist1=['2015','2016','2017','2018']
|
173
|
-
betas3=get_beta_ML('0700.HK','^HSI',yearlist1)
|
174
|
-
|
175
|
-
#==============================================================================
|
176
|
-
def draw2_betas(model,scope,ticker,betas):
|
177
|
-
"""
|
178
|
-
功能:绘制双曲线的贝塔因子变化图
|
179
|
-
输入参数:
|
180
|
-
model: 模型类型, 任意字符串(例如Merrill-Lynch Beta Adjustment)
|
181
|
-
scope: 市场指数, 任意字符串(例如Standard & Poor 500)
|
182
|
-
ticker:股票代码
|
183
|
-
输出:图形
|
184
|
-
"""
|
185
|
-
#仅用作测试,完成后应注释掉
|
186
|
-
#model="Merrill-Lynch Beta Adjustment"
|
187
|
-
#scope="Standard & Poor 500"
|
188
|
-
#ticker="AAPL"
|
189
|
-
|
190
|
-
#取得股票和指数名字,对于非美股可能耗时较长
|
191
|
-
"""
|
192
|
-
import yfinance as yf
|
193
|
-
mktidx= yf.Ticker(scope)
|
194
|
-
idxinfo=mktidx.info
|
195
|
-
idxname=idxinfo['shortName']
|
196
|
-
stkcd=yf.Ticker(ticker)
|
197
|
-
stkinfo=stkcd.info
|
198
|
-
stkname=stkinfo['shortName']
|
199
|
-
title1="\n"+stkname+"\n"+model+"\n(Benchmark on "+idxname+")"
|
200
|
-
"""
|
201
|
-
title1=ticker_name(ticker)+": "+model+"\n(基于"+ticker_name(scope)+")"
|
202
|
-
|
203
|
-
#转换索引类型为DatetimeIndex,便于后续处理
|
204
|
-
"""
|
205
|
-
import pandas as pd
|
206
|
-
betas['Date']=betas.index
|
207
|
-
betas['Date']=pd.to_datetime(betas['Date'])
|
208
|
-
betas.set_index('Date',inplace=True)
|
209
|
-
"""
|
210
|
-
|
211
|
-
#获得列明
|
212
|
-
betalist=betas.columns.values.tolist()
|
213
|
-
beta1=betalist[0]
|
214
|
-
beta2=betalist[1]
|
215
|
-
|
216
|
-
try:
|
217
|
-
plt.plot(betas[beta1],label=beta1,marker='o',color='red')
|
218
|
-
plt.plot(betas[beta2],label=beta2,marker='*',linewidth=2,ls='-.',color='blue')
|
219
|
-
except:
|
220
|
-
print(" #Error(draw2_betas): no available data for drawing!")
|
221
|
-
return
|
222
|
-
plt.axhline(y=1.0,color='b',linestyle=':',label='市场线')
|
223
|
-
plt.title(title1,fontsize=12,fontweight='bold')
|
224
|
-
plt.ylabel("贝塔系数",fontsize=12,fontweight='bold')
|
225
|
-
|
226
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
227
|
-
plt.gca().set_facecolor('whitesmoke')
|
228
|
-
|
229
|
-
#plt.xticks(rotation=30)
|
230
|
-
plt.legend(loc='best')
|
231
|
-
|
232
|
-
import datetime; today = datetime.date.today()
|
233
|
-
plt.xlabel("数据来源:新浪,"+str(today))
|
234
|
-
|
235
|
-
plt.show()
|
236
|
-
|
237
|
-
return
|
238
|
-
|
239
|
-
if __name__=='__main__':
|
240
|
-
model="ML Beta Adjustment"
|
241
|
-
scope="SP500"
|
242
|
-
ticker="AAPL"
|
243
|
-
draw2_betas(model,scope,ticker,betas)
|
244
|
-
|
245
|
-
|
246
|
-
#==============================================================================
|
247
|
-
def get_beta_SW(stkcd,mktidx,yearlist,printout=True,graph=True):
|
248
|
-
"""
|
249
|
-
函数功能:使用SW方法调整一只股票的CAPM模型贝塔系数
|
250
|
-
输入参数:
|
251
|
-
stkcd: 股票代码
|
252
|
-
mktidx: 指数代码
|
253
|
-
yearlist:年度列表,列出其中期间的贝塔系数
|
254
|
-
输出数据:显示CAPM市场模型回归的beta, 以及调整后的beta系数
|
255
|
-
返回数据:CAPM市场模型回归的beta, 以及调整后的beta系数
|
256
|
-
"""
|
257
|
-
|
258
|
-
#仅为测试用,完成后应立即注释掉
|
259
|
-
#stkcd='0700.HK'
|
260
|
-
#mktidx='^HSI'
|
261
|
-
#yearlist=['2015','2016','2017','2018']
|
262
|
-
|
263
|
-
#生成开始结束日期
|
264
|
-
Y4=str(int(yearlist[0])-1)
|
265
|
-
start=Y4+'-01-01'
|
266
|
-
end=yearlist[-1]+'-12-31'
|
267
|
-
|
268
|
-
#读取股价并准备好收益率数据
|
269
|
-
try:
|
270
|
-
R=prepare_capm(stkcd,mktidx,start,end)
|
271
|
-
except:
|
272
|
-
print(" #Error(get_beta_SW): preparing CAPM data failed!")
|
273
|
-
return None
|
274
|
-
|
275
|
-
if (R is None):
|
276
|
-
print(" #Error(get_beta_SW): server time out")
|
277
|
-
return None
|
278
|
-
if (len(R) == 0):
|
279
|
-
print(" #Error(get_beta_SW): server returned empty data")
|
280
|
-
return None
|
281
|
-
|
282
|
-
#用于保存beta(CAPM)和beta(SW)
|
283
|
-
import pandas as pd
|
284
|
-
betas=pd.DataFrame(columns=('Year','Beta(CAPM)','Beta(SW)'))
|
285
|
-
|
286
|
-
#计算Scholes-William调整
|
287
|
-
R['Close_x+1']=R['Close_x'].shift(1)
|
288
|
-
R['Close_x-1']=R['Close_x'].shift(-1)
|
289
|
-
R=R.dropna() #stats.linregress不接受空缺值
|
290
|
-
|
291
|
-
from scipy import stats
|
292
|
-
for year in yearlist:
|
293
|
-
r=R[R['Year']==year]
|
294
|
-
if len(r) != 0:
|
295
|
-
output=stats.linregress(r['Close_x'],r['Close_y'])
|
296
|
-
(beta0,alpha,r_value,p_value,std_err)=output
|
297
|
-
|
298
|
-
output=stats.linregress(r['Close_x+1'],r['Close_y'])
|
299
|
-
(beta1,alpha,r_value,p_value,std_err)=output
|
300
|
-
|
301
|
-
output=stats.linregress(r['Close_x-1'],r['Close_y'])
|
302
|
-
(beta_1,alpha,r_value,p_value,std_err)=output
|
303
|
-
|
304
|
-
output=stats.linregress(r['Close_x-1'],r['Close_x'])
|
305
|
-
(rou,alpha,r_value,p_value,std_err)=output
|
306
|
-
|
307
|
-
beta_SW=(beta_1+beta0+beta1)/(1.0+2.0*rou)
|
308
|
-
row=pd.Series({'Year':year,'Beta(CAPM)':beta0,'Beta(SW)':beta_SW})
|
309
|
-
try:
|
310
|
-
betas=betas.append(row,ignore_index=True)
|
311
|
-
except:
|
312
|
-
betas=betas._append(row,ignore_index=True)
|
313
|
-
|
314
|
-
betas.set_index(["Year"], inplace=True)
|
315
|
-
|
316
|
-
if printout == True: printdf_betas(betas,2)
|
317
|
-
if graph == True:
|
318
|
-
model="贝塔系数的Scholes-Williams调整法"
|
319
|
-
draw2_betas(model,mktidx,stkcd,betas)
|
320
|
-
|
321
|
-
return betas
|
322
|
-
|
323
|
-
|
324
|
-
if __name__=='__main__':
|
325
|
-
yearlist=gen_yearlist('2010','2019')
|
326
|
-
betas_AAPL=get_beta_SW('AAPL','^GSPC',yearlist)
|
327
|
-
|
328
|
-
model="SW Beta Adjustment"
|
329
|
-
scope="SP500"
|
330
|
-
ticker="AAPL"
|
331
|
-
draw2_betas(model,scope,ticker,betas_AAPL)
|
332
|
-
|
333
|
-
#==============================================================================
|
334
|
-
def get_beta_dimson(stkcd,mktidx,yearlist,printout=True,graph=True):
|
335
|
-
"""
|
336
|
-
函数功能:使用Dimson(1979)方法调整一只股票的CAPM模型贝塔系数
|
337
|
-
输入参数:
|
338
|
-
stkcd: 股票代码
|
339
|
-
mktidx: 指数代码
|
340
|
-
yearlist:年度列表,用于计算年度贝塔系数
|
341
|
-
输出数据:显示CAPM市场模型回归的beta, 以及调整后的beta系数
|
342
|
-
返回数据:CAPM的beta, 以及调整后的beta系数
|
343
|
-
"""
|
344
|
-
|
345
|
-
#仅为测试用,完成后应立即注释掉
|
346
|
-
#stkcd='0700.HK'
|
347
|
-
#mktidx='^HSI'
|
348
|
-
#yearlist=['2015','2016','2017','2018']
|
349
|
-
|
350
|
-
#生成开始结束日期
|
351
|
-
Y4=str(int(yearlist[0])-1)
|
352
|
-
start=Y4+'-01-01'
|
353
|
-
end=yearlist[-1]+'-12-31'
|
354
|
-
|
355
|
-
#读取股价并准备好收益率数据
|
356
|
-
try:
|
357
|
-
R=prepare_capm(stkcd,mktidx,start,end)
|
358
|
-
except:
|
359
|
-
print(" #Error(get_beta_dimson): preparing CAPM data failed!")
|
360
|
-
return None
|
361
|
-
|
362
|
-
if (R is None):
|
363
|
-
print(" #Error(get_beta_dimson): server did not respond")
|
364
|
-
return None
|
365
|
-
if (len(R) == 0):
|
366
|
-
print(" #Error(get_beta_dimson): server returned empty data")
|
367
|
-
return None
|
368
|
-
|
369
|
-
#用于保存beta(CAPM)和beta(Dimson)
|
370
|
-
import pandas as pd
|
371
|
-
betas=pd.DataFrame(columns=('Year','Beta(CAPM)','Beta(Dimson)'))
|
372
|
-
|
373
|
-
#计算Dimson(1979)调整
|
374
|
-
R['Close_x+1']=R['Close_x'].shift(1)
|
375
|
-
R['Close_x-1']=R['Close_x'].shift(-1)
|
376
|
-
R=R.dropna()
|
377
|
-
|
378
|
-
from scipy import stats
|
379
|
-
import statsmodels.api as sm
|
380
|
-
for year in yearlist:
|
381
|
-
r=R[R['Year']==year]
|
382
|
-
if len(r) != 0:
|
383
|
-
output=stats.linregress(r['Close_x'],r['Close_y'])
|
384
|
-
(beta_capm,alpha,r_value,p_value,std_err)=output
|
385
|
-
|
386
|
-
#三个解释变量
|
387
|
-
RX=r[['Close_x-1','Close_x','Close_x+1']]
|
388
|
-
X1=sm.add_constant(RX) #要求回归具有截距项
|
389
|
-
Y=r['Close_y']
|
390
|
-
model = sm.OLS(Y,X1) #定义回归模型,X1为多元矩阵
|
391
|
-
results = model.fit() #进行OLS回归
|
392
|
-
|
393
|
-
(alpha,beta_1,beta0,beta1)=results.params #提取回归系数
|
394
|
-
beta_dimson=beta_1+beta0+beta1
|
395
|
-
|
396
|
-
row=pd.Series({'Year':year,'Beta(CAPM)':beta_capm, \
|
397
|
-
'Beta(Dimson)':beta_dimson})
|
398
|
-
try:
|
399
|
-
betas=betas.append(row,ignore_index=True)
|
400
|
-
except:
|
401
|
-
betas=betas._append(row,ignore_index=True)
|
402
|
-
|
403
|
-
betas.set_index(["Year"], inplace=True)
|
404
|
-
|
405
|
-
if printout == True: printdf_betas(betas,2)
|
406
|
-
if graph == True:
|
407
|
-
model="贝塔系数的Dimson调整法"
|
408
|
-
draw2_betas(model,mktidx,stkcd,betas)
|
409
|
-
|
410
|
-
return betas
|
411
|
-
|
412
|
-
if __name__=='__main__':
|
413
|
-
yearlist=gen_yearlist('2010','2019')
|
414
|
-
betas_MSFT=get_beta_dimson('MSFT','^GSPC',yearlist)
|
415
|
-
|
416
|
-
model="Dimson Beta Adjustment"
|
417
|
-
scope="SP500"
|
418
|
-
ticker="MSFT"
|
419
|
-
draw2_betas(model,scope,ticker,betas_MSFT)
|
420
|
-
|
421
|
-
betas_MSFT2=get_beta_dimson('MSFT','^DJI',yearlist)
|
422
|
-
|
423
|
-
model="Dimson Beta Adjustment"
|
424
|
-
scope="DJIA"
|
425
|
-
ticker="MSFT"
|
426
|
-
draw2_betas(model,scope,ticker,betas_MSFT2)
|
427
|
-
|
428
|
-
#==============================================================================
|
429
|
-
#==============================================================================
|
430
|
-
#==============================================================================
|
431
|
-
|
432
|
-
def prepare_hamada_patch_is(ticker):
|
433
|
-
"""
|
434
|
-
在雅虎财经接口获取利润表数据失败时,改从tushare获取
|
435
|
-
获取的项目:所得税费用,税前利润
|
436
|
-
"""
|
437
|
-
import pandas as pd
|
438
|
-
import tushare as ts
|
439
|
-
pro=init_ts()
|
440
|
-
|
441
|
-
#财报期限
|
442
|
-
import datetime
|
443
|
-
today=datetime.date.today()
|
444
|
-
thisyear=today.year
|
445
|
-
fouryrsago=thisyear-4-2
|
446
|
-
fromdate=str(fouryrsago)+"0101"
|
447
|
-
todate=str(thisyear)+"1231"
|
448
|
-
|
449
|
-
#利润表
|
450
|
-
ticker1=ticker.upper()
|
451
|
-
suffix=ticker1[-3:]
|
452
|
-
if suffix in ['.SS']:
|
453
|
-
ticker1=ticker1.replace('.SS','.SH',1)
|
454
|
-
if not (suffix in ['.SS','.SZ']):
|
455
|
-
print(" #Error(prepare_hamada_patch_is): no financials available for",ticker)
|
456
|
-
return None
|
457
|
-
|
458
|
-
fis=pro.income(ts_code=ticker1,start_date=fromdate,end_date=todate)
|
459
|
-
#去除非年报
|
460
|
-
stripfmt=lambda x:(x.strip())[-4:]
|
461
|
-
fis['yrtag']=fis['end_date'].apply(stripfmt)
|
462
|
-
fis1=fis[fis['report_type']=='1'] #合并报表
|
463
|
-
fis2=fis1[fis1['yrtag']=="1231"] #年报
|
464
|
-
fis3=fis2.drop_duplicates(subset=['end_date'],keep='first') #去重
|
465
|
-
fis4=fis3 #保留最新4年,与yfinance结果保持一致
|
466
|
-
fis4.sort_values(by=['end_date'],ascending=True,inplace=True) #升序排序
|
467
|
-
|
468
|
-
#重建索引
|
469
|
-
fis4['date']=pd.to_datetime(fis4['end_date'])
|
470
|
-
fis4.set_index('date',inplace=True)
|
471
|
-
|
472
|
-
#提取需要的项目:所得税费用,税前利润
|
473
|
-
fis4['Income Tax Expense']=fis4['income_tax']
|
474
|
-
fis4['Income Before Tax']=fis4['total_profit']
|
475
|
-
fis5=fis4[['Income Tax Expense','Income Before Tax']].copy()
|
476
|
-
|
477
|
-
return fis5
|
478
|
-
|
479
|
-
if __name__=='__main__':
|
480
|
-
ticker="600519.SS"
|
481
|
-
|
482
|
-
|
483
|
-
#==============================================================================
|
484
|
-
def prepare_hamada_patch_bs(ticker):
|
485
|
-
"""
|
486
|
-
在雅虎财经接口获取资产负债表数据失败时,改从tushare获取
|
487
|
-
获取的项目:负债合计,股东权益合计
|
488
|
-
"""
|
489
|
-
import pandas as pd
|
490
|
-
import tushare as ts
|
491
|
-
pro=init_ts()
|
492
|
-
|
493
|
-
#财报期限
|
494
|
-
import datetime
|
495
|
-
today=datetime.date.today()
|
496
|
-
thisyear=today.year
|
497
|
-
fouryrsago=thisyear-4-2
|
498
|
-
fromdate=str(fouryrsago)+"0101"
|
499
|
-
todate=str(thisyear)+"1231"
|
500
|
-
|
501
|
-
#利润表
|
502
|
-
ticker1=ticker.upper()
|
503
|
-
suffix=ticker1[-3:]
|
504
|
-
if suffix in ['.SS']:
|
505
|
-
ticker1=ticker1.replace('.SS','.SH',1)
|
506
|
-
if not (suffix in ['.SS','.SZ']):
|
507
|
-
print(" #Error(prepare_hamada_patch_bs): no financials available for",ticker)
|
508
|
-
return None
|
509
|
-
|
510
|
-
fis=pro.balancesheet(ts_code=ticker1,start_date=fromdate,end_date=todate)
|
511
|
-
#去除非年报
|
512
|
-
stripfmt=lambda x:(x.strip())[-4:]
|
513
|
-
fis['yrtag']=fis['end_date'].apply(stripfmt)
|
514
|
-
fis1=fis[fis['report_type']=='1'] #合并报表
|
515
|
-
fis2=fis1[fis1['yrtag']=="1231"] #年报
|
516
|
-
fis3=fis2.drop_duplicates(subset=['end_date'],keep='first') #去重
|
517
|
-
fis4=fis3 #保留最新4年,与yfinance结果保持一致
|
518
|
-
fis4.sort_values(by=['end_date'],ascending=True,inplace=True) #升序排序
|
519
|
-
|
520
|
-
#重建索引
|
521
|
-
fis4['date']=pd.to_datetime(fis4['end_date'])
|
522
|
-
fis4.set_index('date',inplace=True)
|
523
|
-
|
524
|
-
#提取需要的项目:所得税费用,税前利润
|
525
|
-
fis4['Total Liab']=fis4['total_liab']
|
526
|
-
fis4['Total Stockholder Equity']=fis4['total_hldr_eqy_inc_min_int']
|
527
|
-
fis5=fis4[['Total Liab','Total Stockholder Equity']].copy()
|
528
|
-
|
529
|
-
return fis5
|
530
|
-
|
531
|
-
if __name__=='__main__':
|
532
|
-
ticker="600519.SS"
|
533
|
-
|
534
|
-
|
535
|
-
#==============================================================================
|
536
|
-
if __name__ =="__main__":
|
537
|
-
ticker='0700.HK'
|
538
|
-
|
539
|
-
def prepare_hamada_yearly_yahoo(ticker):
|
540
|
-
"""
|
541
|
-
功能:从雅虎财经下载财报数据,计算hamada模型需要的因子
|
542
|
-
局限:只能下载最近4年的财报
|
543
|
-
输入:股票代码
|
544
|
-
输出:
|
545
|
-
寻找数据项:所得税费用,税前利润,计算实际税率;
|
546
|
-
总负债,所有者权益,计算财务杠杆
|
547
|
-
数据框, CFLB,贝塔Lev对贝塔Unlev的倍数
|
548
|
-
年度列表
|
549
|
-
"""
|
550
|
-
print("... Searching for financial information, please wait ...")
|
551
|
-
import yfinance as yf
|
552
|
-
stock=yf.Ticker(ticker)
|
553
|
-
|
554
|
-
#利润表
|
555
|
-
try:
|
556
|
-
is0=stock.financials
|
557
|
-
is1=is0.T
|
558
|
-
except:
|
559
|
-
is1=prepare_hamada_patch_is(ticker)
|
560
|
-
|
561
|
-
if len(is0)==0: #yfinance失效
|
562
|
-
is1=prepare_hamada_patch_is(ticker)
|
563
|
-
|
564
|
-
is1['income tax expense']=is1['Income Tax Expense'].astype('float')
|
565
|
-
is1['income before tax']=is1['Income Before Tax'].astype('float')
|
566
|
-
is1['tax rate']=is1['income tax expense']/is1['income before tax']
|
567
|
-
|
568
|
-
import pandas as pd
|
569
|
-
is1['date']=pd.to_datetime(is1.index)
|
570
|
-
is1.set_index(["date"], inplace=True)
|
571
|
-
is2=is1.sort_index(axis=0,ascending=True)
|
572
|
-
tax=pd.DataFrame(is2['tax rate'])
|
573
|
-
|
574
|
-
#资产负债表
|
575
|
-
try:
|
576
|
-
bs0=stock.balance_sheet
|
577
|
-
bs1=bs0.T
|
578
|
-
except:
|
579
|
-
bs1=prepare_hamada_patch_bs(ticker)
|
580
|
-
if len(bs0)==0: #yfinance失效
|
581
|
-
bs1=prepare_hamada_patch_bs(ticker)
|
582
|
-
|
583
|
-
bs1['total liabilities']=bs1['Total Liab'].astype('float')
|
584
|
-
bs1['total equities']=bs1["Total Stockholder Equity"].astype('float')
|
585
|
-
bs1['lev ratio']=bs1['total liabilities']/bs1['total equities']
|
586
|
-
bs1['date']=pd.to_datetime(bs1.index)
|
587
|
-
bs1.set_index(['date'],inplace=True)
|
588
|
-
bs2=bs1.sort_index(axis=0,ascending=True)
|
589
|
-
lev=pd.DataFrame(bs2['lev ratio'])
|
590
|
-
|
591
|
-
#合成,计算
|
592
|
-
fac=pd.merge(lev,tax,how='left',left_index=True,right_index=True)
|
593
|
-
fac['CFLB%']=1/(1+(1/fac['lev ratio'])*(1/abs(1-fac['tax rate'])))*100
|
594
|
-
fac['lev_unlev']=1+fac['lev ratio']*(1-fac['tax rate'])
|
595
|
-
fac['year']=fac.index.strftime("%Y")
|
596
|
-
yearlist=list(fac['year'])
|
597
|
-
|
598
|
-
return fac,yearlist
|
599
|
-
|
600
|
-
if __name__ =="__main__":
|
601
|
-
ticker="600519.SS"
|
602
|
-
ticker="AAPL"
|
603
|
-
ticker='000002.SZ'
|
604
|
-
fac,yl=prepare_hamada_yearly_yahoo("MSFT")
|
605
|
-
|
606
|
-
#==============================================================================
|
607
|
-
if __name__ =="__main__":
|
608
|
-
stkcd='0700.HK'
|
609
|
-
mktidx='^HSI'
|
610
|
-
yearlist=['2015','2016','2017','2018']
|
611
|
-
|
612
|
-
|
613
|
-
def get_beta_hamada(stkcd,mktidx,yearlist,printout=True,graph=True):
|
614
|
-
"""
|
615
|
-
函数功能:使用Hamada(1972)方法,计算无杠杆贝塔系数
|
616
|
-
输入参数:
|
617
|
-
stkcd: 股票代码
|
618
|
-
mktidx: 指数代码
|
619
|
-
yearlist:年度列表,用于计算年度贝塔系数
|
620
|
-
输出数据:显示CAPM市场模型回归的beta, 以及调整后的beta系数
|
621
|
-
返回数据:CAPM的beta, Hamada beta,CFLB(债务融资对CAPM beta系数的贡献率)
|
622
|
-
"""
|
623
|
-
|
624
|
-
#计算Hamada参数,并返回可用的年度列表
|
625
|
-
fac,yearlist=prepare_hamada_yearly_yahoo(stkcd)
|
626
|
-
if fac is None:
|
627
|
-
print("#Error(get_beta_hamada): no financial info available for",stkcd)
|
628
|
-
return None
|
629
|
-
|
630
|
-
#生成开始结束日期
|
631
|
-
Y4=str(int(yearlist[0])-1)
|
632
|
-
start=Y4+'-01-01'
|
633
|
-
end=yearlist[-1]+'-12-31'
|
634
|
-
|
635
|
-
#读取股价并准备好收益率数据
|
636
|
-
try:
|
637
|
-
R=prepare_capm(stkcd,mktidx,start,end)
|
638
|
-
except:
|
639
|
-
print(" #Error(get_beta_hamada): preparing CAPM data failed!")
|
640
|
-
print("Info:",stkcd,mktidx,yearlist)
|
641
|
-
return None
|
642
|
-
|
643
|
-
if (R is None):
|
644
|
-
print(" #Error(get_beta_hamada): server time out")
|
645
|
-
return None
|
646
|
-
if (len(R) == 0):
|
647
|
-
print(" #Error(get_beta_hamada): server returned empty data")
|
648
|
-
return None
|
649
|
-
R=R.dropna()
|
650
|
-
|
651
|
-
#用于保存beta(CAPM)和beta(Hamada)
|
652
|
-
import pandas as pd
|
653
|
-
betas=pd.DataFrame(columns=('Year','Beta(CAPM)','Beta(Unlevered)','CFLB%'))
|
654
|
-
|
655
|
-
from scipy import stats
|
656
|
-
for year in yearlist:
|
657
|
-
r=R[R['Year']==year]
|
658
|
-
if len(r) != 0:
|
659
|
-
output=stats.linregress(r['Close_x'],r['Close_y'])
|
660
|
-
(beta_capm,alpha,r_value,p_value,std_err)=output
|
661
|
-
|
662
|
-
#Hamada无杠杆因子
|
663
|
-
lev_unlev=fac[fac['year']==year]['lev_unlev'].values[0]
|
664
|
-
beta_hamada=beta_capm/lev_unlev
|
665
|
-
cflb=fac[fac['year']==year]['CFLB%'].values[0]
|
666
|
-
|
667
|
-
row=pd.Series({'Year':year,'Beta(CAPM)':beta_capm, \
|
668
|
-
'Beta(Unlevered)':beta_hamada,'CFLB%':cflb})
|
669
|
-
try:
|
670
|
-
betas=betas.append(row,ignore_index=True)
|
671
|
-
except:
|
672
|
-
betas=betas._append(row,ignore_index=True)
|
673
|
-
|
674
|
-
betas.set_index(["Year"], inplace=True)
|
675
|
-
|
676
|
-
if printout == True:
|
677
|
-
printdf_betas(betas,2)
|
678
|
-
if graph == True:
|
679
|
-
model="Hamada Unlevered Beta"
|
680
|
-
draw2_betas(model,mktidx,stkcd,betas)
|
681
|
-
|
682
|
-
#绘制CFLB
|
683
|
-
if len(betas)<=1: return betas
|
684
|
-
|
685
|
-
plt.plot(betas['CFLB%'],marker='o',color='red',lw=3)
|
686
|
-
|
687
|
-
bmin=min(list(betas['CFLB%']))
|
688
|
-
bmax=max(list(betas['CFLB%']))
|
689
|
-
axhmin=(int(bmin/10)+1)*10
|
690
|
-
if bmin <= axhmin <= bmax:
|
691
|
-
plt.axhline(y=axhmin,color='b',linestyle=':')
|
692
|
-
axhmax=(int(bmax/10))*10
|
693
|
-
if bmin <= axhmax <= bmax:
|
694
|
-
plt.axhline(y=axhmax,color='b',linestyle=':')
|
695
|
-
|
696
|
-
title1=stkcd+": Contribution of Financial Leverage to Beta"+ \
|
697
|
-
"\n(Benchmark on Market Index "+mktidx+")"
|
698
|
-
plt.title(title1,fontsize=12,fontweight='bold')
|
699
|
-
plt.ylabel("CFLB %",fontsize=12,fontweight='bold')
|
700
|
-
#plt.legend(loc='best')
|
701
|
-
plt.grid(ls='-.')
|
702
|
-
#查看可用的样式:print(plt.style.available)
|
703
|
-
#样式:bmh(好),classic,ggplot(好,图大),tableau-colorblind10,
|
704
|
-
#样式:seaborn-bright,seaborn-poster,seaborn-whitegrid
|
705
|
-
plt.style.use('bmh')
|
706
|
-
|
707
|
-
plt.gca().set_facecolor('whitesmoke')
|
708
|
-
plt.show()
|
709
|
-
|
710
|
-
return betas
|
711
|
-
|
712
|
-
if __name__=='__main__':
|
713
|
-
stkcd='000002.SZ'
|
714
|
-
mktidx='000001.SS'
|
715
|
-
yearlist=gen_yearlist('2010','2019')
|
716
|
-
betas1=get_beta_hamada('MSFT','^GSPC',yearlist)
|
717
|
-
|
718
|
-
#==============================================================================
|
719
|
-
#==============================================================================
|
720
|
-
#==============================================================================
|
721
|
-
def draw_hamada_factors(stkcd,mktidx,betas):
|
722
|
-
"""
|
723
|
-
功能:绘制Hamada模型因子的变化折线图,企业实际所得税税率,资产负债率,CFLB
|
724
|
-
"""
|
725
|
-
if len(betas)<=1: return
|
726
|
-
|
727
|
-
#计算资产负债率:由 D/E到 D/(A=D+E)
|
728
|
-
betas['Debt/Assets%']=1/(1+1/(betas['Debt Ratio%']/100))*100
|
729
|
-
|
730
|
-
#fig=plt.figure(figsize=(12.8,6.4))
|
731
|
-
fig=plt.figure()
|
732
|
-
ax1=fig.add_subplot(111)
|
733
|
-
ax1.plot(betas['CFLB%'],marker='o',color='green',lw=3,label='CFLB%')
|
734
|
-
ax1.plot(betas['Debt/Assets%'],marker='o',color='red',lw=2,ls='--', \
|
735
|
-
label='Debt/Assets%')
|
736
|
-
ax1.set_ylabel("CFLB%, Debt/Assets%")
|
737
|
-
ax1.legend(loc='upper left')
|
738
|
-
ax1.set_xticklabels(betas.index,rotation=45)
|
739
|
-
|
740
|
-
ax2=ax1.twinx()
|
741
|
-
ax2.plot(betas['Tax Rate%'],marker='o',color='black',lw=2,ls='-.', \
|
742
|
-
label='Income Tax%')
|
743
|
-
ax2.set_ylabel('Income Tax%')
|
744
|
-
ax2.legend(loc='lower right')
|
745
|
-
ax2.set_xticklabels(betas.index,rotation=45)
|
746
|
-
|
747
|
-
title1=stkcd+": Impact of Hamada Factors on Beta"+ \
|
748
|
-
"\n(Benchmark on Market Index "+mktidx+")"
|
749
|
-
plt.title(title1,fontsize=12,fontweight='bold')
|
750
|
-
plt.style.use('ggplot')
|
751
|
-
|
752
|
-
plt.gca().set_facecolor('whitesmoke')
|
753
|
-
plt.show()
|
754
|
-
|
755
|
-
return
|
756
|
-
#==============================================================================
|
757
|
-
#==============================================================================
|
758
|
-
#==============================================================================
|
759
|
-
if __name__ =="__main__":
|
760
|
-
stkcd='0700.HK'
|
761
|
-
stkcd='AAPL'
|
762
|
-
stkcd='GS'
|
763
|
-
stkcd='BA'
|
764
|
-
mktidx='^HSI'
|
765
|
-
stkcd='000002.SZ'
|
766
|
-
stkcd='600606.SS'
|
767
|
-
mktidx='000001.SS'
|
768
|
-
|
769
|
-
def get_beta_hamada2(stkcd,mktidx,printout=True,graph=True):
|
770
|
-
"""
|
771
|
-
函数功能:使用Hamada(1972)方法,计算无杠杆贝塔系数,绘图
|
772
|
-
输入参数:
|
773
|
-
stkcd: 股票代码
|
774
|
-
mktidx: 指数代码
|
775
|
-
输出数据:显示CAPM市场模型回归的beta, 以及调整后的beta系数
|
776
|
-
返回数据:CAPM的beta, Hamada beta,CFLB(债务融资对CAPM beta系数的贡献率)
|
777
|
-
"""
|
778
|
-
|
779
|
-
#计算Hamada参数,并返回可用的年度列表
|
780
|
-
fac=prepare_hamada_yahoo(stkcd)
|
781
|
-
if fac is None:
|
782
|
-
print(" #Error(get_beta_hamada2): no financial info available for",stkcd)
|
783
|
-
return None
|
784
|
-
datecvt=lambda x: str(x.strftime("%Y-%m-%d"))
|
785
|
-
fac['fsdate']=fac.index.date
|
786
|
-
fac['fsdate']=fac['fsdate'].apply(datecvt)
|
787
|
-
|
788
|
-
#生成开始结束日期
|
789
|
-
end0=fac.index[-1]
|
790
|
-
end=date_adjust(end0,adjust=14)
|
791
|
-
start0=fac.index[0]
|
792
|
-
start=date_adjust(start0,adjust=-366)
|
793
|
-
|
794
|
-
#读取股价并准备好收益率数据
|
795
|
-
try:
|
796
|
-
R=prepare_capm(stkcd,mktidx,start,end)
|
797
|
-
except:
|
798
|
-
print(" #Error(get_beta_hamada2): preparing CAPM data failed for",stkcd,mktidx)
|
799
|
-
return None
|
800
|
-
if (R is None) or (len(R) == 0):
|
801
|
-
print(" #Error(get_beta_hamada2): retrieved empty info in CAPM for",stkcd,mktidx)
|
802
|
-
return None
|
803
|
-
R=R.dropna()
|
804
|
-
R['prcdate']=R.index.date
|
805
|
-
R['prcdate']=R['prcdate'].apply(datecvt)
|
806
|
-
|
807
|
-
#用于保存beta(CAPM)和beta(Hamada)
|
808
|
-
import pandas as pd
|
809
|
-
betas=pd.DataFrame(columns=('Date','Beta(CAPM)','Beta(Unlevered)','CFLB%'))
|
810
|
-
fsdatelist=list(fac['fsdate'])
|
811
|
-
from scipy import stats
|
812
|
-
for d in fsdatelist:
|
813
|
-
dstart=date_adjust(d,adjust=-365)
|
814
|
-
r=R[R['prcdate'] >= dstart]
|
815
|
-
r=r[r['prcdate'] <= d]
|
816
|
-
if len(r) != 0:
|
817
|
-
output=stats.linregress(r['Close_x'],r['Close_y'])
|
818
|
-
(beta_capm,alpha,r_value,p_value,std_err)=output
|
819
|
-
|
820
|
-
#Hamada无杠杆因子
|
821
|
-
lev_unlev=fac[fac['fsdate']==d]['lev_unlev'].values[0]
|
822
|
-
beta_hamada=beta_capm/lev_unlev
|
823
|
-
cflb=fac[fac['fsdate']==d]['CFLB%'].values[0]
|
824
|
-
|
825
|
-
row=pd.Series({'Date':d,'Beta(CAPM)':beta_capm, \
|
826
|
-
'Beta(Unlevered)':beta_hamada,'CFLB%':cflb})
|
827
|
-
try:
|
828
|
-
betas=betas.append(row,ignore_index=True)
|
829
|
-
except:
|
830
|
-
betas=betas._append(row,ignore_index=True)
|
831
|
-
betas.set_index(["Date"], inplace=True)
|
832
|
-
|
833
|
-
#打印
|
834
|
-
if printout == True:
|
835
|
-
printdf_betas(betas,2)
|
836
|
-
|
837
|
-
#绘图:两种杠杆对比图,CFLB图
|
838
|
-
if graph == True:
|
839
|
-
|
840
|
-
#绘制Hamada对比图
|
841
|
-
model="滨田无杠杆贝塔系数"
|
842
|
-
draw2_betas(model,mktidx,stkcd,betas)
|
843
|
-
|
844
|
-
#绘制CFLB单图
|
845
|
-
if len(betas)<=1:
|
846
|
-
print(" #Notice(get_beta_hamada2): too few info for graphics of",stkcd)
|
847
|
-
return betas
|
848
|
-
|
849
|
-
plt.plot(betas['CFLB%'],marker='o',color='red',lw=3,label='CFLB%')
|
850
|
-
|
851
|
-
#绘制均值虚线
|
852
|
-
cflb_avg=betas['CFLB%'].mean()
|
853
|
-
cflb_avg_txt='均值: '+str(round(cflb_avg,1))+'%'
|
854
|
-
plt.axhline(y=cflb_avg,color='b',linestyle=':',label=cflb_avg_txt)
|
855
|
-
|
856
|
-
title1=ticker_name(stkcd)+": 财务杠杆对于贝塔系数的贡献度(CFLB)"
|
857
|
-
#plt.title(title1,fontsize=12,fontweight='bold')
|
858
|
-
plt.title(title1)
|
859
|
-
#plt.ylabel("CFLB %",fontsize=12,fontweight='bold')
|
860
|
-
footnote="注: 基于"+ticker_name(mktidx)
|
861
|
-
|
862
|
-
import datetime; today = datetime.date.today()
|
863
|
-
footnote2="\n数据来源: 新浪,"+str(today)
|
864
|
-
plt.xlabel(footnote+footnote2)
|
865
|
-
|
866
|
-
plt.grid(ls='-.')
|
867
|
-
#查看可用的样式:print(plt.style.available)
|
868
|
-
#样式:bmh(好),classic,ggplot(好,图大),tableau-colorblind10,
|
869
|
-
#样式:seaborn-bright,seaborn-poster,seaborn-whitegrid
|
870
|
-
plt.style.use('bmh')
|
871
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
872
|
-
plt.gca().set_facecolor('whitesmoke')
|
873
|
-
plt.legend(loc='best')
|
874
|
-
plt.show(); plt.close()
|
875
|
-
|
876
|
-
#绘制CFLB+财务杠杆双图
|
877
|
-
df1=betas; df2=fac.set_index(["fsdate"])
|
878
|
-
ticker1=ticker2=stkcd
|
879
|
-
colname1='CFLB%'; colname2='lev ratio'
|
880
|
-
label1='CFLB%'; label2='财务杠杆'
|
881
|
-
titletxt=ticker_name(stkcd)+": CFLB与财务杠杆之间的关系"
|
882
|
-
footnote='注: 这里的财务杠杆使用的是负债/所有者权益'
|
883
|
-
|
884
|
-
plot_line2_twinx(df1,ticker1,colname1,label1,df2,ticker2,colname2,label2, \
|
885
|
-
titletxt,footnote+footnote2)
|
886
|
-
|
887
|
-
#绘制CFLB+税率双图
|
888
|
-
#df1=betas; df2=fac.set_index(["fsdate"])
|
889
|
-
#ticker1=ticker2=stkcd
|
890
|
-
colname1='CFLB%'; colname2='tax rate'
|
891
|
-
label1='CFLB%'; label2='实际税率'
|
892
|
-
titletxt=ticker_name(stkcd)+": CFLB与税率之间的关系"
|
893
|
-
footnote='注: 这里使用的是实际税率'
|
894
|
-
|
895
|
-
plot_line2_twinx(df1,ticker1,colname1,label1,df2,ticker2,colname2,label2, \
|
896
|
-
titletxt,footnote+footnote2)
|
897
|
-
|
898
|
-
return betas
|
899
|
-
|
900
|
-
if __name__=='__main__':
|
901
|
-
betas1=get_beta_hamada2('MSFT','^GSPC')
|
902
|
-
|
903
|
-
#==============================================================================
|
904
|
-
if __name__ =="__main__":
|
905
|
-
ticker='0700.HK'
|
906
|
-
ticker="600519.SS"
|
907
|
-
ticker="AAPL"
|
908
|
-
ticker="BA"
|
909
|
-
|
910
|
-
def prepare_hamada_yahoo(ticker):
|
911
|
-
"""
|
912
|
-
功能:从雅虎财经下载财报数据,计算hamada模型需要的因子
|
913
|
-
局限:只能下载最近4年+4个季度的财报
|
914
|
-
输入:股票代码
|
915
|
-
输出:
|
916
|
-
寻找数据项:所得税费用,税前利润,计算实际税率;
|
917
|
-
总负债,所有者权益,计算财务杠杆
|
918
|
-
数据框, CFLB,贝塔Lev对贝塔Unlev的倍数
|
919
|
-
年度列表
|
920
|
-
"""
|
921
|
-
print("...Searching for financial information, please wait ...")
|
922
|
-
|
923
|
-
#利润表
|
924
|
-
try:
|
925
|
-
import siat.financial_statements as fs
|
926
|
-
is1=fs.get_income_statements(ticker)
|
927
|
-
except:
|
928
|
-
print(" #Error(prepare_hamada_yahoo): failed to retrieve income info of",ticker)
|
929
|
-
return None
|
930
|
-
if (is1 is None) or (len(is1)==0):
|
931
|
-
print(" #Error(prepare_hamada_yahoo): retrieve empty income info of",ticker)
|
932
|
-
return None
|
933
|
-
|
934
|
-
is1['tax rate']=is1['TaxRateForCalcs'].astype('float')
|
935
|
-
is1['ticker']=is1.index
|
936
|
-
is1['date']=is1['asOfDate']
|
937
|
-
is1.set_index(["date"], inplace=True)
|
938
|
-
is1.sort_index(axis=0,ascending=True,inplace=True)
|
939
|
-
|
940
|
-
import pandas as pd
|
941
|
-
tax=pd.DataFrame(is1['tax rate'])
|
942
|
-
|
943
|
-
#资产负债表
|
944
|
-
try:
|
945
|
-
bs1=fs.get_balance_sheet(ticker)
|
946
|
-
except:
|
947
|
-
print(" #Error(prepare_hamada_yahoo): failed to retrieve balance sheet of",ticker)
|
948
|
-
return None
|
949
|
-
if (bs1 is None) or (len(bs1)==0):
|
950
|
-
print(" #Error(prepare_hamada_yahoo): retrieve empty balance sheet of",ticker)
|
951
|
-
return None
|
952
|
-
|
953
|
-
bs1['lev ratio']=bs1['TotalLiabilities']/bs1['TotalEquities']
|
954
|
-
bs1['date']=bs1['asOfDate']
|
955
|
-
bs1.set_index(['date'],inplace=True)
|
956
|
-
bs1.sort_index(axis=0,ascending=True,inplace=True)
|
957
|
-
lev=pd.DataFrame(bs1['lev ratio'])
|
958
|
-
|
959
|
-
#合成,计算
|
960
|
-
fac=pd.merge(lev,tax,how='left',left_index=True,right_index=True)
|
961
|
-
fac['CFLB%']=1/(1+(1/fac['lev ratio'])*(1/abs(1-fac['tax rate'])))*100
|
962
|
-
fac['lev_unlev']=1+fac['lev ratio']*(1-fac['tax rate'])
|
963
|
-
|
964
|
-
return fac
|
965
|
-
|
966
|
-
if __name__ =="__main__":
|
967
|
-
fac,yl=prepare_hamada_yahoo("MSFT")
|
968
|
-
|
969
|
-
#==============================================================================
|
970
|
-
|
971
|
-
if __name__=='__main__':
|
972
|
-
tickers = ['000858.SZ','600779.SS','000596.SZ','603589.SS']
|
973
|
-
market_index="000001.SS"
|
974
|
-
|
975
|
-
def compare_mhamada(tickers,market_index="000001.SS"):
|
976
|
-
"""
|
977
|
-
功能:计算多只股票的财务杠杆对贝塔系数贡献度比率CFLB,并绘图对比
|
978
|
-
"""
|
979
|
-
#检查tickers是否为列表且不少于两只股票
|
980
|
-
|
981
|
-
#隐藏print输出
|
982
|
-
import os,sys
|
983
|
-
class HiddenPrints:
|
984
|
-
def __enter__(self):
|
985
|
-
self._original_stdout = sys.stdout
|
986
|
-
sys.stdout = open(os.devnull, 'w')
|
987
|
-
|
988
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
989
|
-
sys.stdout.close()
|
990
|
-
sys.stdout = self._original_stdout
|
991
|
-
|
992
|
-
import pandas as pd
|
993
|
-
df=pd.DataFrame()
|
994
|
-
print("Starting to retrieve and calculate Hamada ratios, please wait ......")
|
995
|
-
for t in tickers:
|
996
|
-
|
997
|
-
with HiddenPrints():
|
998
|
-
df_tmp=get_beta_hamada2(t,market_index,printout=False,graph=False)
|
999
|
-
|
1000
|
-
if df_tmp is None:
|
1001
|
-
print(" Warning(compare_mhamada): data not available for",t)
|
1002
|
-
continue
|
1003
|
-
else:
|
1004
|
-
dft=df_tmp[['CFLB%']]
|
1005
|
-
dft.rename(columns={'CFLB%':ticker_name(t)},inplace=True)
|
1006
|
-
|
1007
|
-
if len(df)==0:
|
1008
|
-
#第一个
|
1009
|
-
df=dft
|
1010
|
-
else:
|
1011
|
-
df=pd.merge(df,dft,left_index=True,right_index=True)
|
1012
|
-
|
1013
|
-
if len(df)==0:
|
1014
|
-
print(" Warning(compare_mhamada): no data available for",tickers)
|
1015
|
-
return None
|
1016
|
-
|
1017
|
-
#绘制多条曲线
|
1018
|
-
import datetime; today = datetime.date.today()
|
1019
|
-
|
1020
|
-
lang=check_language()
|
1021
|
-
if lang == 'English':
|
1022
|
-
|
1023
|
-
y_label='CFLB%'
|
1024
|
-
footnote0='CFLB: Contribution of Financial Leverage to Beta'
|
1025
|
-
x_label=footnote0+'\n'+"Source: sina/stooq, "+str(today)
|
1026
|
-
title_txt="Compare Multiple CFLB"
|
1027
|
-
else:
|
1028
|
-
y_label='CFLB%'
|
1029
|
-
footnote0='CFLB:财务杠杆对贝塔系数的贡献度'
|
1030
|
-
x_label=footnote0+'\n'+"数据来源: 新浪/stooq,"+str(today)
|
1031
|
-
title_txt="比较多只证券的CFLB指标"
|
1032
|
-
|
1033
|
-
draw_lines(df,y_label,x_label,axhline_value=0,axhline_label='', \
|
1034
|
-
title_txt=title_txt,data_label=False)
|
1035
|
-
|
1036
|
-
return df
|
1037
|
-
|
1038
|
-
if __name__=='__main__':
|
1039
|
-
tickers = ['000858.SZ','600779.SS','000596.SZ','603589.SS','000001.SS']
|
1040
|
-
df=compare_mhamada(tickers,market_index)
|
1041
|
-
|
1042
|
-
#==============================================================================
|
1043
|
-
#==============================================================================
|
1044
|
-
#==============================================================================
|
1045
|
-
#==============================================================================
|
1046
|
-
#==============================================================================
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|