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.
Files changed (221) hide show
  1. siat/__init__.py +0 -0
  2. siat/allin.py +8 -0
  3. siat/assets_liquidity.py +0 -0
  4. siat/beta_adjustment.py +0 -0
  5. siat/beta_adjustment_china.py +0 -0
  6. siat/blockchain.py +0 -0
  7. siat/bond.py +0 -0
  8. siat/bond_base.py +0 -0
  9. siat/bond_china.py +0 -0
  10. siat/bond_zh_sina.py +0 -0
  11. siat/capm_beta.py +0 -0
  12. siat/capm_beta2.py +4 -4
  13. siat/common.py +9 -6
  14. siat/compare_cross.py +0 -0
  15. siat/copyrights.py +0 -0
  16. siat/cryptocurrency.py +0 -0
  17. siat/economy.py +0 -0
  18. siat/economy2.py +0 -0
  19. siat/esg.py +0 -0
  20. siat/event_study.py +0 -0
  21. siat/exchange_bond_china.pickle +0 -0
  22. siat/fama_french.py +0 -0
  23. siat/fin_stmt2_yahoo.py +0 -0
  24. siat/financial_base.py +0 -0
  25. siat/financial_statements.py +0 -0
  26. siat/financials.py +0 -0
  27. siat/financials2.py +0 -0
  28. siat/financials_china.py +0 -0
  29. siat/financials_china2.py +0 -0
  30. siat/fund.py +0 -0
  31. siat/fund_china.pickle +0 -0
  32. siat/fund_china.py +0 -0
  33. siat/future_china.py +0 -0
  34. siat/google_authenticator.py +0 -0
  35. siat/grafix.py +55 -4
  36. siat/holding_risk.py +0 -0
  37. siat/luchy_draw.py +0 -0
  38. siat/market_china.py +0 -0
  39. siat/markowitz.py +0 -0
  40. siat/markowitz2.py +1 -0
  41. siat/markowitz2_20250704.py +0 -0
  42. siat/markowitz2_20250705.py +0 -0
  43. siat/markowitz_simple.py +0 -0
  44. siat/ml_cases.py +0 -0
  45. siat/ml_cases_example.py +0 -0
  46. siat/option_china.py +0 -0
  47. siat/option_pricing.py +0 -0
  48. siat/other_indexes.py +0 -0
  49. siat/risk_adjusted_return.py +0 -0
  50. siat/risk_adjusted_return2.py +8 -4
  51. siat/risk_evaluation.py +0 -0
  52. siat/risk_free_rate.py +0 -0
  53. siat/save2docx.py +345 -0
  54. siat/save2pdf.py +145 -0
  55. siat/sector_china.py +0 -0
  56. siat/security_price2.py +0 -0
  57. siat/security_prices.py +168 -6
  58. siat/security_trend.py +0 -0
  59. siat/security_trend2.py +2 -2
  60. siat/stock.py +11 -1
  61. siat/stock_advice_linear.py +0 -0
  62. siat/stock_base.py +0 -0
  63. siat/stock_china.py +0 -0
  64. siat/stock_info.pickle +0 -0
  65. siat/stock_prices_kneighbors.py +0 -0
  66. siat/stock_prices_linear.py +0 -0
  67. siat/stock_profile.py +0 -0
  68. siat/stock_technical.py +0 -0
  69. siat/stooq.py +0 -0
  70. siat/transaction.py +0 -0
  71. siat/translate.py +0 -0
  72. siat/valuation.py +0 -0
  73. siat/valuation_china.py +0 -0
  74. siat/var_model_validation.py +0 -0
  75. siat/yf_name.py +0 -0
  76. {siat-3.10.132.dist-info/licenses → siat-3.11.1.dist-info}/LICENSE +0 -0
  77. {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/METADATA +234 -235
  78. siat-3.11.1.dist-info/RECORD +80 -0
  79. {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/WHEEL +1 -1
  80. {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/top_level.txt +0 -1
  81. build/lib/build/lib/siat/__init__.py +0 -75
  82. build/lib/build/lib/siat/allin.py +0 -137
  83. build/lib/build/lib/siat/assets_liquidity.py +0 -915
  84. build/lib/build/lib/siat/beta_adjustment.py +0 -1058
  85. build/lib/build/lib/siat/beta_adjustment_china.py +0 -548
  86. build/lib/build/lib/siat/blockchain.py +0 -143
  87. build/lib/build/lib/siat/bond.py +0 -2900
  88. build/lib/build/lib/siat/bond_base.py +0 -992
  89. build/lib/build/lib/siat/bond_china.py +0 -100
  90. build/lib/build/lib/siat/bond_zh_sina.py +0 -143
  91. build/lib/build/lib/siat/capm_beta.py +0 -783
  92. build/lib/build/lib/siat/capm_beta2.py +0 -887
  93. build/lib/build/lib/siat/common.py +0 -5360
  94. build/lib/build/lib/siat/compare_cross.py +0 -642
  95. build/lib/build/lib/siat/copyrights.py +0 -18
  96. build/lib/build/lib/siat/cryptocurrency.py +0 -667
  97. build/lib/build/lib/siat/economy.py +0 -1471
  98. build/lib/build/lib/siat/economy2.py +0 -1853
  99. build/lib/build/lib/siat/esg.py +0 -536
  100. build/lib/build/lib/siat/event_study.py +0 -815
  101. build/lib/build/lib/siat/fama_french.py +0 -1521
  102. build/lib/build/lib/siat/fin_stmt2_yahoo.py +0 -982
  103. build/lib/build/lib/siat/financial_base.py +0 -1160
  104. build/lib/build/lib/siat/financial_statements.py +0 -598
  105. build/lib/build/lib/siat/financials.py +0 -2339
  106. build/lib/build/lib/siat/financials2.py +0 -1278
  107. build/lib/build/lib/siat/financials_china.py +0 -4433
  108. build/lib/build/lib/siat/financials_china2.py +0 -2212
  109. build/lib/build/lib/siat/fund.py +0 -629
  110. build/lib/build/lib/siat/fund_china.py +0 -3307
  111. build/lib/build/lib/siat/future_china.py +0 -551
  112. build/lib/build/lib/siat/google_authenticator.py +0 -47
  113. build/lib/build/lib/siat/grafix.py +0 -3636
  114. build/lib/build/lib/siat/holding_risk.py +0 -867
  115. build/lib/build/lib/siat/luchy_draw.py +0 -638
  116. build/lib/build/lib/siat/market_china.py +0 -1168
  117. build/lib/build/lib/siat/markowitz.py +0 -2363
  118. build/lib/build/lib/siat/markowitz2.py +0 -3150
  119. build/lib/build/lib/siat/markowitz2_20250704.py +0 -2969
  120. build/lib/build/lib/siat/markowitz2_20250705.py +0 -3158
  121. build/lib/build/lib/siat/markowitz_simple.py +0 -373
  122. build/lib/build/lib/siat/ml_cases.py +0 -2291
  123. build/lib/build/lib/siat/ml_cases_example.py +0 -60
  124. build/lib/build/lib/siat/option_china.py +0 -3069
  125. build/lib/build/lib/siat/option_pricing.py +0 -1925
  126. build/lib/build/lib/siat/other_indexes.py +0 -409
  127. build/lib/build/lib/siat/risk_adjusted_return.py +0 -1576
  128. build/lib/build/lib/siat/risk_adjusted_return2.py +0 -1900
  129. build/lib/build/lib/siat/risk_evaluation.py +0 -2218
  130. build/lib/build/lib/siat/risk_free_rate.py +0 -351
  131. build/lib/build/lib/siat/sector_china.py +0 -4140
  132. build/lib/build/lib/siat/security_price2.py +0 -727
  133. build/lib/build/lib/siat/security_prices.py +0 -3408
  134. build/lib/build/lib/siat/security_trend.py +0 -402
  135. build/lib/build/lib/siat/security_trend2.py +0 -646
  136. build/lib/build/lib/siat/stock.py +0 -4284
  137. build/lib/build/lib/siat/stock_advice_linear.py +0 -934
  138. build/lib/build/lib/siat/stock_base.py +0 -26
  139. build/lib/build/lib/siat/stock_china.py +0 -2095
  140. build/lib/build/lib/siat/stock_prices_kneighbors.py +0 -910
  141. build/lib/build/lib/siat/stock_prices_linear.py +0 -386
  142. build/lib/build/lib/siat/stock_profile.py +0 -707
  143. build/lib/build/lib/siat/stock_technical.py +0 -3305
  144. build/lib/build/lib/siat/stooq.py +0 -74
  145. build/lib/build/lib/siat/transaction.py +0 -347
  146. build/lib/build/lib/siat/translate.py +0 -5183
  147. build/lib/build/lib/siat/valuation.py +0 -1378
  148. build/lib/build/lib/siat/valuation_china.py +0 -2076
  149. build/lib/build/lib/siat/var_model_validation.py +0 -444
  150. build/lib/build/lib/siat/yf_name.py +0 -811
  151. build/lib/siat/__init__.py +0 -75
  152. build/lib/siat/allin.py +0 -137
  153. build/lib/siat/assets_liquidity.py +0 -915
  154. build/lib/siat/beta_adjustment.py +0 -1058
  155. build/lib/siat/beta_adjustment_china.py +0 -548
  156. build/lib/siat/blockchain.py +0 -143
  157. build/lib/siat/bond.py +0 -2900
  158. build/lib/siat/bond_base.py +0 -992
  159. build/lib/siat/bond_china.py +0 -100
  160. build/lib/siat/bond_zh_sina.py +0 -143
  161. build/lib/siat/capm_beta.py +0 -783
  162. build/lib/siat/capm_beta2.py +0 -887
  163. build/lib/siat/common.py +0 -5360
  164. build/lib/siat/compare_cross.py +0 -642
  165. build/lib/siat/copyrights.py +0 -18
  166. build/lib/siat/cryptocurrency.py +0 -667
  167. build/lib/siat/economy.py +0 -1471
  168. build/lib/siat/economy2.py +0 -1853
  169. build/lib/siat/esg.py +0 -536
  170. build/lib/siat/event_study.py +0 -815
  171. build/lib/siat/fama_french.py +0 -1521
  172. build/lib/siat/fin_stmt2_yahoo.py +0 -982
  173. build/lib/siat/financial_base.py +0 -1160
  174. build/lib/siat/financial_statements.py +0 -598
  175. build/lib/siat/financials.py +0 -2339
  176. build/lib/siat/financials2.py +0 -1278
  177. build/lib/siat/financials_china.py +0 -4433
  178. build/lib/siat/financials_china2.py +0 -2212
  179. build/lib/siat/fund.py +0 -629
  180. build/lib/siat/fund_china.py +0 -3307
  181. build/lib/siat/future_china.py +0 -551
  182. build/lib/siat/google_authenticator.py +0 -47
  183. build/lib/siat/grafix.py +0 -3636
  184. build/lib/siat/holding_risk.py +0 -867
  185. build/lib/siat/luchy_draw.py +0 -638
  186. build/lib/siat/market_china.py +0 -1168
  187. build/lib/siat/markowitz.py +0 -2363
  188. build/lib/siat/markowitz2.py +0 -3150
  189. build/lib/siat/markowitz2_20250704.py +0 -2969
  190. build/lib/siat/markowitz2_20250705.py +0 -3158
  191. build/lib/siat/markowitz_simple.py +0 -373
  192. build/lib/siat/ml_cases.py +0 -2291
  193. build/lib/siat/ml_cases_example.py +0 -60
  194. build/lib/siat/option_china.py +0 -3069
  195. build/lib/siat/option_pricing.py +0 -1925
  196. build/lib/siat/other_indexes.py +0 -409
  197. build/lib/siat/risk_adjusted_return.py +0 -1576
  198. build/lib/siat/risk_adjusted_return2.py +0 -1900
  199. build/lib/siat/risk_evaluation.py +0 -2218
  200. build/lib/siat/risk_free_rate.py +0 -351
  201. build/lib/siat/sector_china.py +0 -4140
  202. build/lib/siat/security_price2.py +0 -727
  203. build/lib/siat/security_prices.py +0 -3408
  204. build/lib/siat/security_trend.py +0 -402
  205. build/lib/siat/security_trend2.py +0 -646
  206. build/lib/siat/stock.py +0 -4284
  207. build/lib/siat/stock_advice_linear.py +0 -934
  208. build/lib/siat/stock_base.py +0 -26
  209. build/lib/siat/stock_china.py +0 -2095
  210. build/lib/siat/stock_prices_kneighbors.py +0 -910
  211. build/lib/siat/stock_prices_linear.py +0 -386
  212. build/lib/siat/stock_profile.py +0 -707
  213. build/lib/siat/stock_technical.py +0 -3305
  214. build/lib/siat/stooq.py +0 -74
  215. build/lib/siat/transaction.py +0 -347
  216. build/lib/siat/translate.py +0 -5183
  217. build/lib/siat/valuation.py +0 -1378
  218. build/lib/siat/valuation_china.py +0 -2076
  219. build/lib/siat/var_model_validation.py +0 -444
  220. build/lib/siat/yf_name.py +0 -811
  221. siat-3.10.132.dist-info/RECORD +0 -218
@@ -1,2076 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- 本模块功能:中国股市估值
4
- 作者:王德宏 (WANG Dehong, Peter)
5
- 作者单位:北京外国语大学国际商学院
6
- 作者邮件:wdehong2000@163.com
7
- 版权所有:王德宏
8
- 用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
9
- 特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
10
- """
11
- #==============================================================================
12
- #屏蔽所有警告性信息
13
- import warnings; warnings.filterwarnings('ignore')
14
- #==============================================================================
15
- from siat.common import *
16
- from siat.translate import *
17
- from siat.grafix import *
18
- from siat.security_prices import *
19
- from siat.security_price2 import *
20
- from siat.sector_china import *
21
-
22
- #==============================================================================
23
-
24
- if __name__ =="__main__":
25
- start='2020-1-1'; end='2022-10-9'
26
- measure='pb'; method='lyr'; value='value'; statistic='median'
27
-
28
- def get_valuation_market_china(start,end,measure='pe',method='lyr',value='value',statistic='median'):
29
- """
30
- 功能:中国A股市场估值趋势,一段时间内
31
- measure:默认市盈率'pe',可选市净率pb
32
- method:默认滚动'ttm',可选静态lyr
33
- value:默认数值'value',可选分位数quantile
34
- statistic:默认使用中位数'median',可选等权重equal-weighted
35
- """
36
-
37
- #检查日期的合理性
38
- result,startpd,endpd=check_period(start,end)
39
- if not result:
40
- print(" #Error(get_valuation_market_china): invalid date period",start,end)
41
- return None
42
-
43
- #检查选项
44
- measure1=measure.lower(); measurelist=['pe','pb']
45
- method1=method.lower(); methodlist=['ttm','lyr']
46
- value1=value.lower(); valuelist=['value','quantile']
47
- statistic1=statistic.lower(); statisticlist=['median','equal-weighted']
48
-
49
- if not (measure1 in measurelist):
50
- print(" #Error(get_valuation_market_china): invalid measurement",measure)
51
- print(" Valid measurement:")
52
- return None
53
- if not (method1 in methodlist):
54
- print(" #Error(get_valuation_market_china): invalid method",method)
55
- print(" Valid method:",methodlist)
56
- return None
57
- if not (value1 in valuelist):
58
- print(" #Error(get_valuation_market_china): invalid value",value)
59
- print(" Valid value:",valuelist)
60
- return None
61
- if not (statistic1 in statisticlist):
62
- print(" #Error(get_valuation_market_china): invalid statistic",statistic)
63
- print(" Valid statistic:",statisticlist)
64
- return None
65
-
66
- # 构造组合矩阵
67
- import pandas as pd
68
- matrix=pd.DataFrame([
69
- #['pe ttm value median','middlePETTM','市盈率(滚动TTM,全A股中位数)'],
70
- #['pe ttm value equal-weighted','averagePETTM','市盈率(滚动TTM,全A股等权平均)'],
71
- #['pe ttm quantile median','quantileInRecent10YearsMiddlePeTtm','市盈率分位数(滚动TTM,全A股中位数,近10年)'],
72
- #['pe ttm quantile equal-weighted','quantileInRecent10YearsAveragePeTtm','市盈率分位数(滚动TTM,全A股等权平均,近10年)'],
73
-
74
- #['pe lyr value median','middlePELYR','市盈率(静态LYR,全A股中位数)'],
75
- #['pe lyr value equal-weighted','averagePELYR','市盈率(静态LYR,全A股等权平均)'],
76
- #['pe lyr quantile median','quantileInRecent10YearsMiddlePeLyr','市盈率分位数(静态LYR,全A股中位数,近10年)'],
77
- #['pe lyr quantile equal-weighted','quantileInRecent10YearsAveragePeLyr','市盈率分位数(静态LYR,全A股等权平均,近10年)'],
78
-
79
- #['pb lyr value median','middlePB','市净率(静态LYR,全A股中位数)'],
80
- #['pb lyr value equal-weighted','equalWeightAveragePB','市净率(静态LYR,全A股等权平均)'],
81
- #['pb lyr quantile median','quantileInRecent10YearsMiddlePB','市净率分位数(静态LYR,全A股中位数,近10年)'],
82
- #['pb lyr quantile equal-weighted','quantileInRecent10YearsEqualWeightAveragePB','市净率分位数(静态LYR,全A股等权平均,近10年)'],
83
-
84
- ['pe ttm value median','middlePETTM','市盈率(TTM,全A股中位数)'],
85
- ['pe ttm value equal-weighted','averagePETTM','市盈率(TTM,全A股等权平均)'],
86
- ['pe ttm quantile median','quantileInRecent10YearsMiddlePeTtm','市盈率分位数(TTM,全A股近十年中位数)'],
87
- ['pe ttm quantile equal-weighted','quantileInRecent10YearsAveragePeTtm','市盈率分位数(TTM,全A股近十年等权平均)'],
88
-
89
- ['pe lyr value median','middlePELYR','市盈率(全A股中位数)'],
90
- ['pe lyr value equal-weighted','averagePELYR','市盈率(全A股等权平均)'],
91
- ['pe lyr quantile median','quantileInRecent10YearsMiddlePeLyr','市盈率分位数(全A股近十年中位数)'],
92
- ['pe lyr quantile equal-weighted','quantileInRecent10YearsAveragePeLyr','市盈率分位数(全A股近十年等权平均)'],
93
-
94
- ['pb lyr value median','middlePB','市净率(全A股中位数)'],
95
- ['pb lyr value equal-weighted','equalWeightAveragePB','市净率(全A股等权平均)'],
96
- ['pb lyr quantile median','quantileInRecent10YearsMiddlePB','市净率分位数(全A股近十年中位数)'],
97
- ['pb lyr quantile equal-weighted','quantileInRecent10YearsEqualWeightAveragePB','市净率分位数(全A股近十年等权平均)'],
98
-
99
- ], columns=['combine','field','desc'])
100
-
101
- #查找组合方式对应的字段名称
102
- combine=measure1+' '+method1+' '+value1+' '+statistic1
103
- try:
104
- field=matrix[matrix['combine']==combine]['field'].values[0]
105
- desc=matrix[matrix['combine']==combine]['desc'].values[0]
106
- except:
107
- #未查到组合
108
- print(" #Error(get_valuation_market_china): parameter combination not available for",combine)
109
- return None
110
-
111
- import akshare as ak
112
-
113
- #获取全A股市场的市盈率
114
- if measure1 == 'pe':
115
- try:
116
- mp = ak.stock_a_ttm_lyr()
117
- except:
118
- #akshare版本需要更新
119
- print(" #Error(get_valuation_market_china): may need to upgrade akshare")
120
- return None
121
-
122
- #截取选定的日期范围
123
- mp['Date']=mp['date']
124
- mp.sort_values(by=['Date'],ascending=True,inplace=True)
125
- mp.set_index(['Date'],inplace=True)
126
-
127
- try:
128
- mp1=mp[(mp.index >= startpd) & (mp.index <=endpd)]
129
- except:
130
- startpd=startpd.date()
131
- endpd=endpd.date()
132
- mp1=mp[(mp.index >= startpd) & (mp.index <=endpd)]
133
-
134
- mp9=mp1[['date',field,'close']]
135
- mp9['field']=mp9[field]
136
- mp9['index']=mp9['close']
137
- mp9['index name']="沪深300指数"
138
- mp9['measure']=measure1
139
- mp9['method']=method1
140
- mp9['value']=value1
141
- mp9['statistic']=statistic1
142
- mp9['desc']=desc
143
-
144
-
145
- #获取全A股市场的市净率
146
- if measure1 == 'pb':
147
- try:
148
- mp = ak.stock_a_all_pb()
149
- except:
150
- #akshare版本需要更新
151
- print(" #Error(get_valuation_market_china): may need to upgrade akshare")
152
- return None
153
-
154
- #截取选定的日期范围
155
- mp['Date']=mp['date']
156
- mp.sort_values(by=['Date'],ascending=True,inplace=True)
157
- mp.set_index(['Date'],inplace=True)
158
-
159
- try:
160
- mp1=mp[(mp.index >= startpd) & (mp.index <=endpd)]
161
- except:
162
- startpd=startpd.date()
163
- endpd=endpd.date()
164
- mp1=mp[(mp.index >= startpd) & (mp.index <=endpd)]
165
-
166
- mp9=mp1[['date',field,'close']]
167
- mp9['field']=mp9[field]
168
- mp9['index']=mp9['close']
169
- mp9['index name']="上证综合指数"
170
- mp9['measure']=measure1
171
- mp9['method']=method1
172
- mp9['value']=value1
173
- mp9['statistic']=statistic1
174
- mp9['desc']=desc
175
-
176
- df=mp9[['date','field','index','index name','measure','method','value','statistic','desc']]
177
-
178
- return df
179
-
180
- if __name__ =="__main__":
181
- start='2020-1-1'; end='2022-10-9'
182
- df=get_valuation_market_china(start,end,measure='pe',method='lyr',value='value',statistic='median')
183
-
184
- #==============================================================================
185
- if __name__ =="__main__":
186
- start='2024-1-1'; end='2025-3-31'
187
- indicator='pe'
188
- method='ttm'
189
- value='value'
190
- statistic='median'
191
-
192
- indicator=['pb','pe']
193
- method=['lyr','ttm']
194
- value=['value','quantile']
195
- statistic=['median','equal-weighted']
196
-
197
- twinx=False; loc1='best'; loc2='best'
198
- power=0; twinx=False; average_value=True
199
- annotate=False; annotate_value=False; plus_sign=True
200
- mark_top=False; mark_bottom=False; mark_end=False
201
-
202
- loc1='upper left'; loc2='lower right'
203
- facecolor='whitesmoke'; maxticks=20
204
-
205
-
206
-
207
- def valuation_china(start='MRY',end='today',indicator='pe', \
208
- method='lyr',value='value',statistic='median', \
209
- average_value=False,show_index=False, \
210
- power=0,twinx=False, \
211
-
212
- band_area='', \
213
- attention_value='',attention_value_area='', \
214
- attention_point='',attention_point_area='', \
215
-
216
- annotate=False,annotate_value=False,plus_sign=True, \
217
- mark_start=False,mark_top=False,mark_bottom=False,mark_end=False, \
218
-
219
- loc1='upper left',loc2='lower right', \
220
- facecolor='whitesmoke',maxticks=20):
221
- """
222
- ===========================================================================
223
- 功能:比较中国全A股市场的估值指标变化趋势
224
- start: 开始日期,格式YYYY-MM-DD。
225
- 或者简写版,例如'MRY'近1年、'L3Y'表示近3年等。
226
- end: 结束日期,默认今天。
227
-
228
- indicator: 估值指标市盈率'pe'或市净率'pb',或其组合。不支持股息率
229
- method: 滚动'ttm或静态取样'lyr',或其组合
230
- value: 直接采用估值指标数值'value'或分位数'quantile',或其组合
231
- statistic: 采用中位数'median'或等权均值'equal-weighted',或其组合
232
- twinx:是否使用双轴绘图法,默认否False。
233
- 如果同时选择了市盈率'pe'或市净率'pb',建议打开该选项True。
234
- loc1/loc2:图例1/2的位置,默认自动决定'best'。
235
- 如果自动位置不理想,可以手动设置位置。
236
- """
237
- print(" Working on valuating China stock market ... ...")
238
-
239
- import pandas as pd
240
- #处理日期
241
- start,end=start_end_preprocess(start,end)
242
-
243
- # 情形1:双indicator:双指标,PE+PB
244
- if isinstance(indicator,list) and len(indicator) >= 2:
245
- indicator=indicator[:2]
246
- if isinstance(method,list): method=method[0]
247
-
248
- if isinstance(value,list): value=value[0]
249
- if isinstance(statistic,list): statistic=statistic[0]
250
-
251
- df=None
252
- for i in indicator:
253
- if 'pb' != i.lower():
254
- dftmp=get_valuation_market_china(start,end,measure=i.lower(), \
255
- method=method, \
256
- value=value,statistic=statistic)
257
- else:
258
- # pb不支持ttm
259
- dftmp=get_valuation_market_china(start,end,measure=i.lower(), \
260
- method='lyr', \
261
- value=value,statistic=statistic)
262
-
263
- val_desc=dftmp['desc'].values[0]
264
- method_desc=dftmp['method'].values[0]
265
- """
266
- if method_desc == 'lyr': val_desc=val_desc+'静态'
267
- elif method_desc == 'ttm':
268
- #val_desc=val_desc+'动态'
269
- val_desc=val_desc
270
- if value == 'value': val_desc=val_desc+'数值'
271
- elif value == 'quantile': val_desc=val_desc+'分位数'
272
- """
273
- dftmp[val_desc]=dftmp['field']
274
- dftmp2=dftmp[[val_desc]]
275
-
276
- if df is None:
277
- df=dftmp2
278
- else:
279
- df=pd.merge(df,dftmp2,left_index=True,right_index=True)
280
-
281
- # 情形2:单indicator+双method:ttm+lyr(仅适用于pe;pb仅适用lyr);
282
- elif isinstance(method,list) and len(method) >= 2:
283
- method=method[:2]
284
- if isinstance(indicator,list): indicator=indicator[0]
285
-
286
- if isinstance(value,list): value=value[0]
287
- if isinstance(statistic,list): statistic=statistic[0]
288
-
289
- df=None
290
- for i in method:
291
- if (indicator.lower() == 'pe') or \
292
- (indicator.lower() == 'pb' and i.lower() == 'lyr'):
293
- dftmp=get_valuation_market_china(start,end,measure=indicator.lower(), \
294
- method=i.lower(), \
295
- value=value,statistic=statistic)
296
-
297
- val_desc=dftmp['desc'].values[0]
298
- method_desc=dftmp['method'].values[0]
299
- """
300
- if method_desc == 'lyr': val_desc=val_desc+'静态'
301
- elif method_desc == 'ttm':
302
- #val_desc=val_desc+'动态'
303
- val_desc=val_desc
304
- if value == 'value': val_desc=val_desc+'数值'
305
- elif value == 'quantile': val_desc=val_desc+'分位数'
306
- """
307
- dftmp[val_desc]=dftmp['field']
308
- dftmp2=dftmp[[val_desc]]
309
-
310
- if df is None:
311
- df=dftmp2
312
- else:
313
- df=pd.merge(df,dftmp2,left_index=True,right_index=True)
314
-
315
- # 情形3:单indicator+单method+双value;
316
- elif isinstance(value,list) and len(value) >= 2:
317
- value=value[:2]
318
- if isinstance(indicator,list): indicator=indicator[0]
319
- if isinstance(method,list): method=method[0]
320
- if indicator == 'pb': method='lyr'
321
-
322
- if isinstance(statistic,list): statistic=statistic[0]
323
-
324
- df=None
325
- for i in value:
326
- dftmp=get_valuation_market_china(start,end,measure=indicator.lower(), \
327
- method=method.lower(), \
328
- value=i.lower(),statistic=statistic)
329
-
330
- val_desc=dftmp['desc'].values[0]
331
- method_desc=dftmp['method'].values[0]
332
- """
333
- if method_desc == 'lyr': val_desc=val_desc+'静态'
334
- elif method_desc == 'ttm':
335
- #val_desc=val_desc+'动态'
336
- val_desc=val_desc
337
- if value == 'value': val_desc=val_desc+'数值'
338
- elif value == 'quantile': val_desc=val_desc+'分位数'
339
- """
340
- dftmp[val_desc]=dftmp['field']
341
- dftmp2=dftmp[[val_desc]]
342
-
343
- if df is None:
344
- df=dftmp2
345
- else:
346
- df=pd.merge(df,dftmp2,left_index=True,right_index=True)
347
-
348
- # 数值与分位数差距较大,使用twinx=True
349
- twinx=True
350
-
351
- # 情形4:单indicator+单method+单value+双statistic;
352
- elif isinstance(statistic,list) and len(statistic) >= 2:
353
- statistic=statistic[:2]
354
- if isinstance(indicator,list): indicator=indicator[0]
355
- if isinstance(method,list): method=method[0]
356
- if indicator == 'pb': method='lyr'
357
-
358
- df=None
359
- for i in statistic:
360
- dftmp=get_valuation_market_china(start,end,measure=indicator.lower(), \
361
- method=method.lower(), \
362
- value=value.lower(),statistic=i.lower())
363
-
364
- val_desc=dftmp['desc'].values[0]
365
- method_desc=dftmp['method'].values[0]
366
- """
367
- if method_desc == 'lyr': val_desc=val_desc+'静态'
368
- elif method_desc == 'ttm':
369
- #val_desc=val_desc+'动态'
370
- val_desc=val_desc
371
- if value == 'value': val_desc=val_desc+'数值'
372
- elif value == 'quantile': val_desc=val_desc+'分位数'
373
- """
374
- dftmp[val_desc]=dftmp['field']
375
- dftmp2=dftmp[[val_desc]]
376
-
377
- if df is None:
378
- df=dftmp2
379
- else:
380
- df=pd.merge(df,dftmp2,left_index=True,right_index=True)
381
-
382
- # 情形5:单indicator+单method+单value+单statistic;
383
- else:
384
- if isinstance(indicator,list): indicator=indicator[0]
385
- if isinstance(method,list): method=method[0]
386
- if indicator == 'pb': method='lyr'
387
-
388
- if isinstance(value,list): value=value[0]
389
- if isinstance(statistic,list): statistic=statistic[0]
390
-
391
- dftmp=get_valuation_market_china(start,end,measure=indicator.lower(), \
392
- method=method.lower(), \
393
- value=value.lower(),statistic=statistic.lower())
394
-
395
- val_desc=dftmp['desc'].values[0]
396
- method_desc=dftmp['method'].values[0]
397
- """
398
- if method_desc == 'lyr': val_desc=val_desc+'静态'
399
- elif method_desc == 'ttm':
400
- #val_desc=val_desc+'动态'
401
- val_desc=val_desc
402
- if value == 'value': val_desc=val_desc+'数值'
403
- elif value == 'quantile': val_desc=val_desc+'分位数'
404
- """
405
- dftmp[val_desc]=dftmp['field']
406
- if show_index:
407
- index_desc=dftmp['index name'].values[0]
408
- dftmp[index_desc]=dftmp['index']
409
- df=dftmp[[val_desc,index_desc]]
410
-
411
- # 指数与指标差异大,设定twinx=True
412
- twinx=True
413
- else:
414
- df=dftmp[[val_desc]]
415
-
416
- # 绘图
417
- import datetime; todaydt = datetime.date.today()
418
- sourcetxt=text_lang("数据来源:legulegu","Data source: legulegu")
419
- footnote=sourcetxt+', '+str(todaydt)
420
-
421
- if show_index:
422
- # 显示股票市场指数
423
- twinx=True
424
-
425
- titletxt=text_lang("中国A股市场估值趋势","China A-share Market Valuation")
426
- ylabeltxt=text_lang("估值指标","Valuation Indicator")
427
-
428
- if len(list(df)) == 1:
429
- colname=collabel=list(df)[0]
430
- #titletxt=titletxt+': '+colname
431
- ylabeltxt=colname
432
- plot_line(df,colname,collabel,ylabeltxt,titletxt,footnote, \
433
- power=power, \
434
- attention_value=attention_value,attention_value_area=attention_value_area, \
435
- attention_point=attention_point,attention_point_area=attention_point_area, \
436
- average_value=average_value, \
437
-
438
- loc=loc1, \
439
- mark_start=True,mark_top=True,mark_bottom=True,mark_end=mark_end, \
440
- facecolor=facecolor,maxticks=maxticks)
441
-
442
- #elif twinx and len(list(df)) >= 2:
443
- elif twinx != False and len(list(df)) >= 2:
444
- ticker1=ticker2=''
445
- colname1=label1=list(df)[0]; df1=df[[colname1]]
446
- colname2=label2=list(df)[1]; df2=df[[colname2]]
447
-
448
- plot_line2(df1,ticker1,colname1,label1, \
449
- df2,ticker2,colname2,label2, \
450
- ylabeltxt,titletxt,footnote, \
451
- power=power,datatag1=False,datatag2=False,yscalemax=5, \
452
- zeroline=False, \
453
- twinx=twinx, \
454
- yline=999,attention_value_area='', \
455
- xline=999,attention_point_area='', \
456
- resample_freq='H',loc1=loc1,loc2=loc2, \
457
- color1='red',color2='blue',facecolor='whitesmoke', \
458
- maxticks=maxticks)
459
- else:
460
- x_label=footnote
461
- axhline_value=0; axhline_label=''
462
- draw_lines(df,ylabeltxt,x_label,axhline_value,axhline_label,titletxt, \
463
- band_area=band_area,loc=loc1, \
464
- attention_value=attention_value,attention_value_area=attention_value_area, \
465
- attention_point=attention_point,attention_point_area=attention_point_area, \
466
- annotate=annotate,annotate_value=annotate_value,plus_sign=False, \
467
- mark_top=mark_top,mark_bottom=mark_bottom,mark_end=mark_end, \
468
- facecolor=facecolor,maxticks_enable=True,maxticks=maxticks)
469
-
470
- return df
471
-
472
-
473
- if __name__ =="__main__":
474
- start='2020-1-1'; end='2022-10-9'
475
- measures=['pb','pe']; methods='lyr'; values='value'; statistics='median'
476
-
477
- measures='pe'; methods=['lyr','ttm']; values='value'; statistics='median'
478
-
479
-
480
- def valuation_market_china(start='MRY',end='today',measures=['pe','pb'], \
481
- methods='lyr',values='value',statistics='median', \
482
- twinx=False,loc1='best',loc2='best'):
483
- """
484
- ===========================================================================
485
- 功能:比较中国全A股市场的估值指标变化趋势
486
- start: 开始日期,格式YYYY-MM-DD。
487
- 或者简写版,例如'MRY'近1年、'L3Y'表示近3年等。
488
- end: 结束日期,默认今天。
489
-
490
- measures: 估值指标市盈率'pe'或市净率'pb',不支持股息率
491
- methods: 滚动'ttm/静态取样'lyr'.
492
- values: 直接采用估值指标数值'value'或分位数'quantile'
493
- statistics: 采用中位数'median'或等权均值'equal-weighted'
494
- twinx:是否使用双轴绘图法,默认否False。
495
- 如果同时选择了市盈率'pe'或市净率'pb',建议打开该选项True。
496
- loc1/loc2:图例1/2的位置,默认自动决定'best'。
497
- 如果自动位置不理想,可以手动设置位置。
498
- """
499
- #处理日期
500
- start,end=start_end_preprocess(start,end)
501
-
502
- #解析比较的指标,以第一个双指标为准
503
- found2=False
504
- parmlist=['measures','methods','values','statistics']
505
- for v in parmlist:
506
-
507
- #如果是一个字符串
508
- if isinstance(eval(v),str):
509
- globals()[v+'1']=eval(v)
510
- globals()[v+'2']=eval(v)
511
-
512
- #如果是一个列表
513
- if isinstance(eval(v),list):
514
- num = len(eval(v))
515
- #print("num=",num)
516
-
517
- if num == 0:
518
- print(" #Error(valuation_market_china):need at least 1 parameter for",eval(v))
519
- return None,None
520
-
521
- if num == 1:
522
- globals()[v+'1']=eval(v)[0]
523
- globals()[v+'2']=eval(v)[0]
524
-
525
-
526
- if num >= 2:
527
- globals()[v+'1']=eval(v)[0]
528
- globals()[v+'2']=eval(v)[1]
529
- found2=True
530
-
531
- if not found2:
532
- print(" #Warning(valuation_market_china):parameters mismatch among",parmlist)
533
- #return None,None
534
- """
535
- print("measures1=",measures1,"measures2=",measures2)
536
- print("methods1=",methods1,"methods2=",methods2)
537
- print("values1=",values1,"values2=",values2)
538
- print("statistics1=",statistics1,"statistics2=",statistics2)
539
- """
540
-
541
- if values=='value':
542
- ylabeltxt='估值比率'
543
- else:
544
- ylabeltxt='分位数'
545
-
546
- print(" Working on valuating China stock market ... ...")
547
-
548
- titletxt='中国全A股市场估值的变化趋势'
549
-
550
- import datetime
551
- today = datetime.date.today()
552
- footnote="数据来源: legulegu,"+str(today)
553
-
554
- #获取指标1
555
- df1=get_valuation_market_china(start,end,measure=measures1,method=methods1,value=values1,statistic=statistics1)
556
- if df1 is None:
557
- print(" #Error(valuation_market_china):no data available for the combine of",measures1,methods1,values1,statistics1)
558
- return None,None
559
-
560
- ticker1=df1['desc'].values[0]
561
- colname1='field'
562
- label1=''
563
-
564
- if not found2:
565
- plot_line(df1,colname1,ticker1,ylabeltxt,titletxt,footnote, \
566
- power=0,loc=loc1, \
567
- date_fmt='%Y-%m-%d')
568
- return df1,None
569
-
570
- #获取指标2
571
- df2=get_valuation_market_china(start,end,measure=measures2,method=methods2,value=values2,statistic=statistics2)
572
- if df2 is None:
573
- print(" #Error(valuation_market_china):data unavailable for the combine of",measures2,methods2,values2,statistics2)
574
- return None,None
575
-
576
- ticker2=df2['desc'].values[0]
577
- colname2='field'
578
- label2=''
579
-
580
- if twinx == 'auto':
581
- twinx=False
582
-
583
- max1=df1[colname1].max()
584
- max2=df2[colname2].max()
585
- bili=max1/max2
586
- if (bili > 2) or (bili < 0.5):
587
- twinx=True
588
-
589
- plot2_line2(df1,ticker1,colname1,label1, \
590
- df2,ticker2,colname2,label2, \
591
- ylabeltxt,titletxt,footnote, \
592
- twinx=twinx,loc1=loc1,loc2=loc2)
593
- #清除变量
594
- #"""
595
- for v in parmlist:
596
- del globals()[v+'1'],globals()[v+'2']
597
- #"""
598
-
599
- return df1,df2
600
-
601
- if __name__ =="__main__":
602
- start='2020-1-1'; end='2022-10-9'
603
- measures=['pe','pb']; methods='lyr'; values='value'; statistics='median'
604
- df1,df2=valuation_market_china(start,end,measures=['pe','pb'],methods='lyr',values='value',statistics='median')
605
-
606
- #==============================================================================
607
- # 行业估值:申万宏远行业,韭圈儿
608
- #==============================================================================
609
- if __name__=='__main__':
610
- top=5
611
- vtype='PE'
612
- vsorting='quantile'
613
- printout=True
614
- graph=True
615
- axisamp=3
616
- px=False
617
-
618
- def industry_valuation_sw(top=10,vtype='PE',vsorting='quantile', \
619
- graph=True,axisamp=1.2,px=False):
620
- """
621
- 功能:列示申万行业指数估值最高和最低的行业
622
- vtype: PE, PB, dividend
623
- vsorting: 分位数绝对值
624
- """
625
-
626
- import akshare as ak
627
- # 如果出错,升级一下akshare
628
- df = ak.index_value_name_funddb()
629
-
630
- # 筛选申万行业指数
631
- substr='(申万)'
632
- df['申万标志']=df['指数名称'].apply(lambda x: substr in x)
633
- df1=df[df['申万标志']]
634
- df1['行业代码']=df1['指数代码'].apply(lambda x: x[:6])
635
- df1['行业名称']=df1['指数名称'].apply(lambda x: x[:x.index(substr)])
636
-
637
- #检查估值类型
638
- typelist=['pe','pb','dividend']
639
- vtypeu=vtype.lower()
640
- if not (vtypeu in typelist):
641
- print(" #Warning(industry_valuation_sw): unsupported valuation type",vtype)
642
- print(" Supported types:",typelist)
643
- return None
644
-
645
- #检查排序类型
646
- sortlist=['quantile','value']
647
- vsortingu=vsorting.lower()
648
- if not (vsortingu in sortlist):
649
- print(" #Warning(industry_valuation_sw): unsupported sorting type",vsorting)
650
- print(" Supported types:",sortlist)
651
- return None
652
-
653
- #排序:高位优先
654
- if vtypeu == 'pe':
655
- if vsortingu == 'value':
656
- df2=df1.sort_values(by='最新PE',ascending=False)
657
- collist=['行业名称','最新PE','PE分位','最新PB','PB分位','股息率','股息率分位','行业代码']
658
- colname='最新PE'
659
- else:
660
- df2=df1.sort_values(by='PE分位',ascending=False)
661
- collist=['行业名称','PE分位','最新PE','PB分位','最新PB','股息率','股息率分位','行业代码']
662
- colname='PE分位'
663
-
664
- if vtypeu == 'pb':
665
- if vsortingu == 'value':
666
- df2=df1.sort_values(by='最新PB',ascending=False)
667
- collist=['行业名称','最新PB','PB分位','最新PE','PE分位','股息率','股息率分位','行业代码']
668
- colname='最新PB'
669
- else:
670
- df2=df1.sort_values(by='PB分位',ascending=False)
671
- collist=['行业名称','PB分位','最新PB','最新PE','PE分位','股息率','股息率分位','行业代码']
672
- colname='PB分位'
673
-
674
- if vtypeu == 'dividend':
675
- if vsortingu == 'value':
676
- df2=df1.sort_values(by='股息率',ascending=False)
677
- collist=['行业名称','股息率','股息率分位','最新PB','PB分位','最新PE','PE分位','行业代码']
678
- colname='股息率'
679
- else:
680
- df2=df1.sort_values(by='股息率分位',ascending=False)
681
- collist=['行业名称','股息率分位','股息率','最新PB','PB分位','最新PE','PE分位','行业代码']
682
- colname='股息率分位'
683
-
684
- df2.reset_index(drop=True,inplace=True)
685
- df2.index=df2.index+1
686
- df3=df2[collist]
687
-
688
- if top > 0:
689
- df4=df3.head(top)
690
- elif top < 0:
691
- df4=df3.tail(-top)
692
- else:
693
- df4=df3
694
- df5=df4.set_index('行业名称')
695
- df5.sort_values(by=colname,ascending=True,inplace=True)
696
-
697
- #绘图
698
- if graph:
699
- if top > 0:
700
- prefix="最高的"
701
- else:
702
- prefix="最低的"
703
-
704
- if vsortingu=='quantile':
705
- suffix="数百分比"
706
- footnote1="分位数表示历史上比当前便宜的百分比,"
707
- else:
708
- suffix="数值"
709
- footnote1=''
710
-
711
- titletxt="估值分析:基于"+colname+suffix+","+prefix+str(abs(top))+"个行业"
712
-
713
- import datetime
714
- today = datetime.date.today()
715
- footnote0="注:申万宏源行业分类,"
716
- footnote2="数据来源: 申万宏源/韭圈儿,"+str(today)
717
- footnote=footnote0+footnote1+footnote2
718
-
719
- if not px:
720
- fig=plot_barh(df5,colname,titletxt,footnote,axisamp=axisamp)
721
- else:
722
- fig=plot_barh2(df5,colname,titletxt,footnote)
723
-
724
- return df2
725
-
726
- if __name__=='__main__':
727
- df=industry_valuation_sw(top=10,vtype='PE',vsorting='quantile',axisamp=2.3)
728
- df=industry_valuation_sw(top=-10,vtype='PE',vsorting='quantile',axisamp=1.2)
729
- df=industry_valuation_sw(top=10,vtype='PE',vsorting='value',axisamp=1.5)
730
- df=industry_valuation_sw(top=-10,vtype='PE',vsorting='value',axisamp=1.6)
731
-
732
- df=industry_valuation_sw(top=10,vtype='PB',vsorting='quantile',axisamp=2.1)
733
- df=industry_valuation_sw(top=-10,vtype='PB',vsorting='quantile',axisamp=1.2)
734
- df=industry_valuation_sw(top=10,vtype='PB',vsorting='value',axisamp=2)
735
- df=industry_valuation_sw(top=-10,vtype='PB',vsorting='value',axisamp=1.6)
736
-
737
- df=industry_valuation_sw(top=10,vtype='dividend',vsorting='quantile',axisamp=32)
738
- df=industry_valuation_sw(top=-10,vtype='dividend',vsorting='quantile',axisamp=1.2)
739
- df=industry_valuation_sw(top=10,vtype='dividend',vsorting='value',axisamp=2)
740
- df=industry_valuation_sw(top=-10,vtype='dividend',vsorting='value',axisamp=1.3)
741
- #==============================================================================
742
- #==============================================================================
743
- if __name__=='__main__':
744
- industry='食品饮料'
745
- industry='生猪养殖'
746
- industry='国防军工'
747
-
748
- industry='801853.SW'
749
-
750
- start='2021-1-1'
751
- end='2022-11-15'
752
- vtype='PE'
753
-
754
- graph=True
755
- loc='best'
756
-
757
- df=industry_valuation_history_sw_daily(industry,start,end,vtype)
758
-
759
- def industry_valuation_history_sw_daily(industry,start,end,vtype='PE', \
760
- graph=True,loc='best', \
761
- error_message=True):
762
- """
763
- 功能:绘制一个申万行业的日历史估值趋势,不支持二级三级行业分类
764
- vtype: PE, PB, dividend
765
-
766
- *** 注意:必须安装插件ipywidgets
767
- 如果出现下列错误信息:Error displaying widget: model not found
768
- 先卸载再重新安装插件ipywidgets
769
- """
770
- #检查日期期间
771
- result,start1,end1=check_period(start,end)
772
- if not result:
773
- print(" #Warning(industry_valuation_history_sw_daily): invalid date period",start,end)
774
- return None
775
-
776
- #检查估值类型
777
- typelist=['pe','pb','dividend']
778
- vtypeu=vtype.lower()
779
- if not (vtypeu in typelist):
780
- print(" #Warning(industry_valuation_history_sw_daily): unsupported valuation type",vtype)
781
- print(" Supported types:",typelist)
782
- return None
783
-
784
- vtypelist=['pe','pb','dividend']
785
- typelist=['市盈率','市净率','股息率']
786
- pos=vtypelist.index(vtypeu)
787
- vtypes=typelist[pos]
788
-
789
- # 适配industry名称/代码
790
- industry_split=industry.split('.')
791
- split1=industry.split('.')[0]
792
- split1_name=industry_sw_code(split1)
793
-
794
- #industry情形1:无后缀,名称
795
- if len(industry_split)==1 and not split1.isdigit():
796
-
797
- if not split1_name is None: #是申万名称
798
- sindustry=industry+'(申万)'
799
- else: #不是申万名称
800
- sindustry=industry
801
- #industry情形2:数字
802
- else:
803
- if not split1_name is None: #是申万代码
804
- sindustry=industry_sw_name(split1)+'(申万)'
805
- else: #不是申万代码
806
- index_val=ak.index_value_name_funddb()
807
- sindustry=index_val[index_val['指数代码']==industry]['指数名称'].values[0]
808
-
809
- # 获取行业估值历史数据
810
- import akshare as ak
811
- try:
812
- # symbol:指数名称,申万行业名称需要加后缀(申万),仅支持申万一级行业和部分二级行业,不支持申万三级行业
813
- # 支持的指数查看:ak.index_value_name_funddb()
814
- df = ak.index_value_hist_funddb(symbol=sindustry, indicator=vtypes)
815
- except:
816
- if error_message:
817
- print(" #Warning(industry_valuation_history_sw_daily): failed to access index",sindustry)
818
- return None
819
-
820
- import pandas as pd
821
- df['date']=pd.to_datetime(df['日期'])
822
- df.set_index('date',inplace=True)
823
- df1=df[[vtypes]]
824
-
825
- #筛选期间
826
- df2=df1[(df1.index >= start1) & (df1.index <= end1)]
827
-
828
- #绘图
829
- if graph:
830
- df2['平均值']=df2[vtypes].mean()
831
- df2['中位数']=df2[vtypes].median()
832
-
833
- titletxt="行业估值趋势:"+industry_sw_name(industry)+','+vtypes
834
-
835
- footnote0="注:申万宏源行业指数,"
836
- footnote1=''
837
- import datetime
838
- today = datetime.date.today()
839
- footnote2="数据来源: 申万宏源/韭圈儿,"+str(today)
840
- footnote=footnote0+footnote1+footnote2
841
-
842
- colname=vtypes
843
- collabel=vtypes
844
- ylabeltxt=vtypes
845
-
846
- draw_lines(df2,y_label=ylabeltxt,x_label=footnote, \
847
- axhline_value=0,axhline_label='', \
848
- title_txt=titletxt,data_label=False,resample_freq='D')
849
-
850
- return df2
851
-
852
- if __name__=='__main__':
853
- df=industry_valuation_history_sw_daily(industry,start,end,vtype='PE')
854
- df=industry_valuation_history_sw_daily(industry,start,end,vtype='PB')
855
- df=industry_valuation_history_sw_daily(industry,start,end,vtype='dividend')
856
-
857
- df=industry_valuation_history_sw_daily(industry='纺织服饰',start=start,end=end,vtype='PE')
858
- df=industry_valuation_history_sw_daily(industry='纺织服饰',start=start,end=end,vtype='PB')
859
- df=industry_valuation_history_sw_daily(industry='纺织服饰',start=start,end=end,vtype='dividend')
860
-
861
- #==============================================================================
862
- #==============================================================================
863
- if __name__=='__main__':
864
- adate='2023-12-14'
865
-
866
- def get_last_friday(adate):
867
- """
868
- 功能:给定日期,找出上一个周五的日期,配合申万指数估值函数使用
869
- """
870
-
871
- result,fdate=check_date2(adate)
872
- if not result:
873
- return None
874
-
875
- import pendulum
876
- wrk=pendulum.parse(fdate).day_of_week
877
-
878
- import datetime
879
- todaydt = datetime.date.today().strftime('%Y-%m-%d')
880
- if fdate > todaydt:
881
- fdate=todaydt
882
-
883
- if wrk==5:
884
- if fdate != todaydt:
885
- adj=-1
886
- else:
887
- adj=-(2+wrk)
888
- elif wrk==6:
889
- adj=-1
890
- else:
891
- adj=-(2+wrk)
892
- last_fri=date_adjust(fdate,adjust=adj)
893
-
894
- return last_fri
895
-
896
- if __name__=='__main__':
897
- start='2023-1-1'
898
- end='2023-12-14'
899
- get_all_friday(start,end)
900
-
901
- def get_all_friday(start,end):
902
- """
903
- 功能:获取start和end之间所有的周五日期,配合申万指数估值函数使用
904
- """
905
- #import pandas as pd
906
- start_fri=get_last_friday(start)
907
- end_fri=get_last_friday(end)
908
-
909
- import akshare as ak
910
- wrk_df=ak.index_analysis_week_month_sw("week")
911
- wrk_df['Date']=wrk_df['date'].apply(lambda x:x.strftime('%Y-%m-%d'))
912
- frilist=list(wrk_df['Date'])
913
-
914
- period_frilist=[]
915
- for f in frilist:
916
- if (f >= start_fri) and (f <= end_fri):
917
- period_frilist=period_frilist+[f]
918
-
919
- return period_frilist
920
-
921
- #==============================================================================
922
-
923
- if __name__=='__main__':
924
- industry='食品饮料'
925
- industry='白酒Ⅱ'
926
- start='2023-10-1'
927
- end='2023-12-15'
928
- vtype='PE'
929
-
930
- graph=True
931
- loc='best'
932
- df=industry_valuation_history_sw_weekly(industry,start,end,vtype)
933
-
934
- def industry_valuation_history_sw_weekly(industry,start,end,vtype='PE', \
935
- graph=True,loc='best'):
936
- """
937
- 功能:绘制一个申万行业的周历史估值趋势,支持申万"市场表征", "一级行业", "二级行业", "风格指数"
938
- 不支持三级行业,若为非二级行业,转为industry_valuation_history_sw_daily函数处理日数据,专注处理二级行业周数据
939
- vtype: PE, PB, dividend
940
-
941
- """
942
- #检查日期期间
943
- result,start1,end1=check_period(start,end)
944
- if not result:
945
- print(" #Warning(industry_valuation_history_sw_weekly): invalid date period",start,end)
946
- return None
947
- fridays=get_all_friday(start,end)
948
-
949
- #检查估值类型
950
- typelist=['pe','pb','dividend']
951
- vtypeu=vtype.lower()
952
- if not (vtypeu in typelist):
953
- print(" #Warning(industry_valuation_history_sw_weekly): unsupported valuation type",vtype)
954
- print(" Supported types:",typelist)
955
- return None
956
-
957
- vtypelist=['pe','pb','dividend']
958
- typelist=['市盈率','市净率','股息率']
959
- pos=vtypelist.index(vtypeu)
960
- vtypes=typelist[pos]
961
-
962
- #分辨申万行业代码类别
963
- sw_codes=industry_sw_list()
964
- sw_codes['type_name']=''
965
- sw_codes['type_name']=sw_codes.apply(lambda x: '市场表征' if x['type']=='F' else x['type_name'],axis=1)
966
- sw_codes['type_name']=sw_codes.apply(lambda x: '一级行业' if x['type']=='I' else x['type_name'],axis=1)
967
- sw_codes['type_name']=sw_codes.apply(lambda x: '二级行业' if x['type']=='T' else x['type_name'],axis=1)
968
- sw_codes['type_name']=sw_codes.apply(lambda x: '风格指数' if x['type']=='S' else x['type_name'],axis=1)
969
- sw_codes['type_name']=sw_codes.apply(lambda x: '三级行业' if x['type']=='3' else x['type_name'],axis=1)
970
-
971
- industry1=industry.split('.')[0]
972
- industry_name_flag=industry_code_flag=False
973
- type_name=''
974
- try:
975
- type_name=sw_codes[sw_codes['name']==industry1]['type_name'].values[0]
976
- industry_name_flag=True
977
- except:
978
- try:
979
- type_name=sw_codes[sw_codes['code']==industry1]['type_name'].values[0]
980
- industry_code_flag=True
981
- except:
982
- print(" #Warning(industry_valuation_history_sw_weekly): not a Shenwan index for",industry)
983
- #return None
984
-
985
- if type_name=='三级行业':
986
- print(" #Warning(industry_valuation_history_sw_weekly): currently does not support Shenwan 3rd_level industry",industry)
987
- return None
988
-
989
- if not (type_name in ['二级行业','风格指数']):
990
- df=industry_valuation_history_sw_daily(industry=industry,start=start,end=end,vtype=vtype, \
991
- graph=graph,loc=loc,error_message=False)
992
- if not (df is None):
993
- return df
994
-
995
- # 获取行业估值历史周数据:啰嗦方法,需要反复下载,容易出ipywidgets引起的错误,需要安装之或卸载后重新安装
996
- import pandas as pd
997
- import akshare as ak
998
- df=None
999
- for f in fridays:
1000
- f1=f[:4]+f[5:7]+f[8:]
1001
- try:
1002
- dft=ak.index_analysis_weekly_sw(symbol=type_name, date=f1)
1003
- except:
1004
- continue
1005
-
1006
- """
1007
- dft的结构:
1008
- ['指数代码','指数名称','发布日期','收盘指数','成交量','涨跌幅','换手率',
1009
- '市盈率','市净率','均价','成交额占比','流通市值','平均流通市值','股息率']
1010
- """
1011
-
1012
- if not (dft is None):
1013
- if industry_name_flag:
1014
- dft2=dft[dft['指数名称']==industry1]
1015
- if industry_code_flag:
1016
- dft2=dft[dft['指数代码']==industry1]
1017
-
1018
- if df is None:
1019
- df=dft2
1020
- else:
1021
- df=pd.concat([df,dft2])
1022
-
1023
- df['date']=pd.to_datetime(df['发布日期'])
1024
- df.set_index('date',inplace=True)
1025
- df1=df[[vtypes]]
1026
-
1027
- #筛选期间
1028
- #df2=df1[(df1.index >= start1) & (df1.index <= end1)]
1029
- df2=df1.dropna()
1030
-
1031
- #绘图
1032
- if graph:
1033
- df2['平均值']=df2[vtypes].mean()
1034
- df2['中位数']=df2[vtypes].median()
1035
-
1036
- titletxt="行业估值趋势:"+industry+','+vtypes
1037
-
1038
- footnote0="注:申万宏源行业指数,"
1039
- footnote1=''
1040
- import datetime
1041
- today = datetime.date.today()
1042
- footnote2="数据来源: 申万宏源,"+str(today)
1043
- footnote=footnote0+footnote1+footnote2
1044
-
1045
- colname=vtypes
1046
- collabel=vtypes
1047
- ylabeltxt=vtypes
1048
-
1049
- draw_lines(df2,y_label=ylabeltxt,x_label=footnote, \
1050
- axhline_value=0,axhline_label='', \
1051
- title_txt=titletxt,data_label=False,resample_freq='D')
1052
-
1053
- return df2
1054
-
1055
- #==============================================================================
1056
-
1057
- if __name__=='__main__':
1058
- industry='食品饮料'
1059
- industry='白酒Ⅱ'
1060
- industry='绩优股指数'
1061
- industry='801853.SW'
1062
-
1063
- start='2023-10-1'
1064
- end='2023-12-15'
1065
- vtype='PE'
1066
-
1067
- graph=True
1068
- loc='best'
1069
- df=industry_valuation_history_sw(industry,start,end,vtype)
1070
-
1071
- def industry_valuation_history_sw(industry,start,end,vtype='PE', \
1072
- graph=True,loc='best'):
1073
- """
1074
- 功能:绘制一个申万行业的日历史估值趋势,支持申万"市场表征", "一级行业", "二级行业", "风格指数"
1075
- 不支持三级行业,若为非二级行业,转为industry_valuation_history_sw_daily函数处理日数据,专注处理二级行业日数据
1076
- vtype: PE, PB, dividend
1077
-
1078
- """
1079
- #检查日期期间
1080
- result,start1,end1=check_period(start,end)
1081
- if not result:
1082
- print(" #Warning(industry_valuation_history_sw): invalid date period",start,end)
1083
- return None
1084
-
1085
- #检查估值类型
1086
- typelist=['pe','pb','dividend']
1087
- vtypeu=vtype.lower()
1088
- if not (vtypeu in typelist):
1089
- print(" #Warning(industry_valuation_history_sw): unsupported valuation type",vtype)
1090
- print(" Supported types:",typelist)
1091
- return None
1092
-
1093
- vtypelist=['pe','pb','dividend']
1094
- typelist=['市盈率','市净率','股息率']
1095
- pos=vtypelist.index(vtypeu)
1096
- vtypes=typelist[pos]
1097
-
1098
- #分辨申万行业代码类别
1099
- sw_codes=industry_sw_list()
1100
- sw_codes['type_name']=''
1101
- sw_codes['type_name']=sw_codes.apply(lambda x: '市场表征' if x['type']=='F' else x['type_name'],axis=1)
1102
- sw_codes['type_name']=sw_codes.apply(lambda x: '一级行业' if x['type']=='I' else x['type_name'],axis=1)
1103
- sw_codes['type_name']=sw_codes.apply(lambda x: '二级行业' if x['type']=='T' else x['type_name'],axis=1)
1104
- sw_codes['type_name']=sw_codes.apply(lambda x: '风格指数' if x['type']=='S' else x['type_name'],axis=1)
1105
- sw_codes['type_name']=sw_codes.apply(lambda x: '三级行业' if x['type']=='3' else x['type_name'],axis=1)
1106
-
1107
- industry1=industry.split('.')[0]
1108
- industry_name_flag=industry_code_flag=False
1109
- try:
1110
- type_name=sw_codes[sw_codes['name']==industry1]['type_name'].values[0]
1111
- industry_name_flag=True; industry_name=industry1
1112
- except:
1113
- try:
1114
- type_name=sw_codes[sw_codes['code']==industry1]['type_name'].values[0]
1115
- industry_code_flag=True
1116
- industry_name=sw_codes[sw_codes['code']==industry1]['name'].values[0]
1117
- except:
1118
- print(" #Error(industry_valuation_history_sw): Shenwan industry not found for",industry)
1119
- return None
1120
-
1121
- if type_name=='三级行业':
1122
- print(" #Error(industry_valuation_history_sw): currently does not support Shenwan 3rd_level industry",industry)
1123
- return None
1124
-
1125
- #if not (type_name=='二级行业'):
1126
- if not (type_name in ['二级行业','风格指数']):
1127
- df=industry_valuation_history_sw_daily(industry=industry,start=start,end=end,vtype=vtype, \
1128
- graph=graph,loc=loc)
1129
- if not (df is None):
1130
- return df
1131
-
1132
- # 获取行业估值历史周数据:笨方法,反复下载。易出ipywidgets引起的错误,可卸载后重装
1133
- import pandas as pd
1134
- import akshare as ak
1135
- start2=start1.strftime('%Y-%m-%d')
1136
- end2 =end1.strftime('%Y-%m-%d')
1137
- pdate=end2
1138
- dstep=7
1139
- df=None
1140
- while (pdate >= start2) or (abs(date_delta(pdate,start2)) < dstep):
1141
- if pdate >= start2:
1142
- enddate=pdate
1143
- fromdate=date_adjust(pdate,adjust=-dstep)
1144
- else:
1145
- enddate=start2
1146
- fromdate=pdate
1147
-
1148
- try:
1149
- fromdate1=fromdate[:4]+fromdate[5:7]+fromdate[8:10]
1150
- enddate1=enddate[:4]+enddate[5:7]+enddate[8:10]
1151
- dft=ak.index_analysis_daily_sw(symbol=type_name,start_date=fromdate1,end_date=enddate1)
1152
- except:
1153
- dft=None
1154
- """
1155
- try:
1156
- fromdate1=fromdate[:4]+fromdate[5:7]+fromdate[8:10]
1157
- enddate1=enddate[:4]+enddate[5:7]+enddate[8:10]
1158
- dft=ak.index_analysis_daily_sw(symbol=type_name,start_date=fromdate1,end_date=enddate1)
1159
- except:
1160
- continue
1161
- """
1162
- """
1163
- dft的结构:
1164
- ['指数代码','指数名称','发布日期','收盘指数','成交量','涨跌幅','换手率',
1165
- '市盈率','市净率','均价','成交额占比','流通市值','平均流通市值','股息率']
1166
- """
1167
-
1168
- if not (dft is None):
1169
- if industry_name_flag:
1170
- dft2=dft[dft['指数名称']==industry1]
1171
- if industry_code_flag:
1172
- dft2=dft[dft['指数代码']==industry1]
1173
-
1174
- if df is None:
1175
- df=dft2
1176
- else:
1177
- df=pd.concat([df,dft2])
1178
-
1179
- # 开始下一轮循环
1180
- pdate=date_adjust(fromdate,adjust=-1)
1181
-
1182
- df.sort_values('发布日期',ascending=True,inplace=True)
1183
- df.drop_duplicates(inplace=True)
1184
-
1185
- #df=df[df.index >= start1]
1186
- #df.dropna(inplace=True)
1187
-
1188
- df['date']=pd.to_datetime(df['发布日期'])
1189
- df.set_index('date',inplace=True)
1190
- df1=df[[vtypes]]
1191
-
1192
- #筛选期间
1193
- #df2=df1[(df1.index >= start1) & (df1.index <= end1)]
1194
- df2=df1
1195
-
1196
- #绘图
1197
- if graph:
1198
- df2['平均值']=df2[vtypes].mean()
1199
- df2['中位数']=df2[vtypes].median()
1200
-
1201
- titletxt="行业估值趋势:"+industry_name+','+vtypes
1202
-
1203
- footnote0="注:申万行业分类指数,"
1204
- footnote1=''
1205
- import datetime
1206
- today = datetime.date.today()
1207
- footnote2="数据来源: 申万宏源,"+str(today)
1208
- footnote=footnote0+footnote1+footnote2
1209
-
1210
- colname=vtypes
1211
- collabel=vtypes
1212
- ylabeltxt=vtypes
1213
-
1214
- draw_lines(df2,y_label=ylabeltxt,x_label=footnote, \
1215
- axhline_value=0,axhline_label='', \
1216
- title_txt=titletxt,data_label=False,resample_freq='D')
1217
-
1218
- return df2
1219
-
1220
- #==============================================================================
1221
- #==============================================================================
1222
- if __name__=='__main__':
1223
- industries=['食品饮料','纺织服饰']
1224
- industries=['银行','国有大型银行Ⅱ','股份制银行Ⅱ','城商行Ⅱ','农商行Ⅱ']
1225
- start='2023-12-1'
1226
- end='2023-12-15'
1227
- vtypes='PE'
1228
-
1229
- industries='纺织服饰'
1230
- vtypes=['PE','PB']
1231
-
1232
- graph=True
1233
- loc1='lower left'
1234
- loc2='upper right'
1235
-
1236
- df5=compare_industry_valuation_sw(industries,start,end,vtypes)
1237
-
1238
- def compare_industry_valuation_sw(industries,start,end,vtypes='PE', \
1239
- graph=True,loc1='best',loc2='best'):
1240
- """
1241
- 功能:比较多个申万行业或者两个指标的历史估值趋势
1242
- 条件:若industries为列表且多个,则取vtype的第一个值;
1243
- 若industries为字符串或者列表但只有一个,则取vtype的前两个值比较,双轴
1244
- vtypes: PE, PB, dividend
1245
-
1246
- """
1247
-
1248
- #检查日期期间的合理性
1249
-
1250
-
1251
- vtypelist=['pe','pb','dividend']
1252
- typelist=['市盈率','市净率','股息率']
1253
-
1254
- #检查行业个数:多个行业+单指标
1255
- if isinstance(industries,list) & (len(industries) >= 2):
1256
-
1257
- if isinstance(vtypes,str):
1258
- vtype=vtypes
1259
- elif isinstance(vtypes,list):
1260
- vtype=vtypes[0]
1261
-
1262
- vtypeu=vtype.lower()
1263
- pos=vtypelist.index(vtypeu)
1264
- vtypec=typelist[pos]
1265
-
1266
- import pandas as pd
1267
- df=pd.DataFrame()
1268
- for i in industries:
1269
- # debug
1270
- print(" Searching valuation info for",i,'\b, which may take time ...')
1271
- dft=industry_valuation_history_sw(i,start=start,end=end,vtype=vtype,graph=False)
1272
- if not (dft is None):
1273
- dft.rename(columns={vtypec:i},inplace=True)
1274
- if len(df) == 0:
1275
- df=dft
1276
- else:
1277
- df=pd.merge(df,dft,how='outer',left_index=True,right_index=True)
1278
- else:
1279
- continue
1280
-
1281
- #绘图
1282
- if graph:
1283
- titletxt="行业估值趋势对比:"+vtypec
1284
-
1285
- footnote0="注:申万宏源行业指数,"
1286
- footnote1=''
1287
- import datetime
1288
- today = datetime.date.today()
1289
- footnote2="数据来源: 申万宏源/韭圈儿,"+str(today)
1290
- footnote=footnote0+footnote1+footnote2
1291
-
1292
- ylabeltxt=vtypec
1293
-
1294
- draw_lines(df,y_label=ylabeltxt,x_label=footnote, \
1295
- axhline_value=0,axhline_label='', \
1296
- title_txt=titletxt,data_label=False,resample_freq='D')
1297
-
1298
- return df
1299
-
1300
- #检查行业个数:一个行业+双指标
1301
- if ((isinstance(industries,str) | (isinstance(industries,list) & (len(industries) == 1)))) \
1302
- & (isinstance(vtypes,list) & (len(vtypes) >= 2)):
1303
-
1304
- if isinstance(industries,str):
1305
- industry=industries
1306
- elif isinstance(industries,list):
1307
- industry=industries[0]
1308
-
1309
- if isinstance(vtypes,str):
1310
- ivtypelist=[vtypes]
1311
- elif isinstance(vtypes,list):
1312
- ivtypelist=vtypes[:2]
1313
-
1314
- import pandas as pd
1315
- df=pd.DataFrame()
1316
- for t in ivtypelist:
1317
-
1318
- dft=industry_valuation_history_sw(industry,start=start,end=end,vtype=t,graph=False)
1319
-
1320
- if len(df) == 0:
1321
- df=dft
1322
- else:
1323
- df=pd.merge(df,dft,how='outer',left_index=True,right_index=True)
1324
-
1325
- #绘图
1326
- if graph:
1327
- titletxt="行业估值趋势对比:"+industry
1328
-
1329
- footnote0="注:申万宏源行业指数,"
1330
- footnote1=''
1331
- import datetime
1332
- today = datetime.date.today()
1333
- footnote2="数据来源: 申万宏源/韭圈儿,"+str(today)
1334
- footnote=footnote0+footnote1+footnote2
1335
-
1336
- collist=list(df)
1337
- colname1=label1=collist[0]
1338
- colname2=label2=collist[1]
1339
-
1340
- plot_line2(df,'',colname1,label1, \
1341
- df,'',colname2,label2, \
1342
- ylabeltxt='',titletxt=titletxt,footnote=footnote, \
1343
- twinx=True, \
1344
- resample_freq='D',loc1=loc1,loc2=loc2, \
1345
- color1='red',color2='blue')
1346
-
1347
- return df
1348
-
1349
- #检查行业个数:一个行业+一个指标
1350
- if ((isinstance(industries,str) | (isinstance(industries,list) & (len(industries) == 1)))) \
1351
- & ((isinstance(vtypes,str) | (isinstance(vtypes,list) & (len(vtypes) == 1)))):
1352
-
1353
- if isinstance(industries,str):
1354
- industry=industries
1355
- elif isinstance(industries,list):
1356
- industry=industries[0]
1357
-
1358
- if isinstance(vtypes,str):
1359
- ivtype=vtypes
1360
- elif isinstance(vtypes,list):
1361
- ivtype=vtypes[0]
1362
-
1363
- df=industry_valuation_history_sw(industry,start,end,vtype=ivtype, \
1364
- graph=graph,loc=loc1)
1365
- return df
1366
-
1367
- if __name__=='__main__':
1368
- df=compare_industry_valuation_sw(industries=['纺织服饰','国防军工','食品饮料'], \
1369
- start='2017-1-1',end='2022-11-15', \
1370
- vtypes='PE',loc1='lower left',loc2='upper right')
1371
-
1372
- df=compare_industry_valuation_sw(industries=['纺织服饰'], \
1373
- start='2017-1-1',end='2022-11-15', \
1374
- vtypes='PE',loc1='lower left',loc2='upper right')
1375
-
1376
- df=compare_industry_valuation_sw(industries=['纺织服饰'], \
1377
- start='2017-1-1',end='2022-11-15', \
1378
- vtypes=['PE','PB'],loc1='lower left',loc2='upper right')
1379
-
1380
- #==============================================================================
1381
- #==============================================================================
1382
- if __name__=='__main__':
1383
- end='2022-11-18'
1384
- start=date_adjust(end,-365*5)
1385
- valuation=['PE','PB','dividend']
1386
- return_delay=['Annual','Quarterly','Monthly']
1387
- industries='all'
1388
-
1389
- lo_est_betas_list,betas_list,idfall=valuation2return_sw(start,end,valuation=valuation,return_delay=return_delay)
1390
-
1391
- def valuation2return_sw2(start,end,valuation=['PE','PB','dividend'], \
1392
- return_delay=['Annual','Quarterly','Monthly'], \
1393
- industries='all'):
1394
- """
1395
- 废弃!!!
1396
- 功能:测试估值指标对滞后一段时间收益率的影响。若正向(负向)影响,行业估值未低估(高估)
1397
- start, end: 测试期间
1398
- valuation: 估值指标,可为单项指标或列表。市盈率PE, 市净率PB, 股息率dividend
1399
- return_delay: 滞后时间长度,可为单项指标或列表。Monthly一个月=21天,Quarterly一个季度=63天,Annual一年=252天
1400
- """
1401
- # 检查日期的合理性
1402
-
1403
- # 检查估值指标的类型
1404
- valuationlist=['pe','pb','dividend']
1405
- if isinstance(valuation,str):
1406
- valuation_list=[valuation]
1407
- elif isinstance(valuation,list):
1408
- valuation_list=valuation
1409
- for v in valuation_list:
1410
- if not (v.lower() in valuationlist):
1411
- print(" #Warning(valuation2return_sw): unsupported type of valuation:",v)
1412
- print(" supported types of valuation:",valuationlist)
1413
- return None
1414
-
1415
- # 检查估值指标的类型
1416
- return_delaylist=['annual','quarterly','monthly']
1417
- measurelist=['Annual Ret','Quarterly Ret','Monthly Ret']
1418
- shiftlist=[252,63,21]
1419
-
1420
- if isinstance(return_delay,str):
1421
- return_delay_list=[return_delay]
1422
- elif isinstance(return_delay,list):
1423
- return_delay_list=return_delay
1424
-
1425
- measure_list=[]
1426
- shift_days_list=[]
1427
- for v in return_delay_list:
1428
- if not (v.lower() in return_delaylist):
1429
- print(" #Warning(valuation2return_sw): unsupported type of return delay:",v)
1430
- print(" supported types of return delay:",return_delaylist)
1431
- return None
1432
-
1433
- pos=return_delaylist.index(v.lower())
1434
- measure=measurelist[pos]
1435
- measure_list=measure_list+[measure]
1436
- shift_days=shiftlist[pos]
1437
- shift_days_list=shift_days_list+[shift_days]
1438
-
1439
- # 获取行业历史数据,本步骤所需时间较长=========================================
1440
- industry_data=get_industry_sw('I')
1441
- if not (industries.lower() == 'all'):
1442
- industry_codes=industry_sw_codes(industries)
1443
- else:
1444
- industry_codes=list(set(list(industry_data['ticker'])))
1445
-
1446
- # 计算基础数据,本步骤所需时间较长============================================
1447
- idf,idfall=calc_industry_sw(industry_data,start,end)
1448
-
1449
- # 构造回归数据,进行回归,记录回归结果
1450
- import pandas as pd
1451
- from scipy import stats
1452
-
1453
- #屏蔽函数内print信息输出的类
1454
- import os, sys
1455
- class HiddenPrints:
1456
- def __enter__(self):
1457
- self._original_stdout = sys.stdout
1458
- sys.stdout = open(os.devnull, 'w')
1459
-
1460
- def __exit__(self, exc_type, exc_val, exc_tb):
1461
- sys.stdout.close()
1462
- sys.stdout = self._original_stdout
1463
-
1464
- betas_list=[]
1465
- lo_est_betas_list=[]
1466
- print("Calculating industry valuations, it may take great time, please wait ...")
1467
- for m in measure_list:
1468
-
1469
- #计算收益率
1470
- ret_df=compare_industry_sw(idfall,industry_codes,measure=m,graph=False)
1471
- industry_names=list(ret_df)
1472
- pos=measure_list.index(m)
1473
- d=shift_days_list[pos]
1474
-
1475
- ret_collist=list(ret_df)
1476
- ret_reg=pd.DataFrame()
1477
- # 构造用于回归的数据结构
1478
- for i in ret_collist:
1479
- tmpdf=ret_df[[i]]
1480
- tmpdf['行业']=i
1481
- tmpdf.rename(columns={i:'ret'},inplace=True)
1482
-
1483
- if len(ret_reg)==0:
1484
- ret_reg=tmpdf
1485
- else:
1486
- try:
1487
- ret_reg=ret_reg.append(tmpdf)
1488
- except:
1489
- ret_reg=ret_reg._append(tmpdf)
1490
-
1491
- # 计算估值指标
1492
- for val in valuation_list:
1493
- with HiddenPrints():
1494
- val_df=compare_industry_valuation_sw(industry_names,start=start,end=end,
1495
- vtypes=val,graph=False)
1496
-
1497
- # 滞后估值指标
1498
- val_df2=val_df.shift(d)
1499
- val_collist=list(val_df2)
1500
- val_reg=pd.DataFrame()
1501
-
1502
- for i in val_collist:
1503
- tmpdf=val_df2[[i]]
1504
- tmpdf['行业']=i
1505
- tmpdf.rename(columns={i:val},inplace=True)
1506
-
1507
- if len(val_reg)==0:
1508
- val_reg=tmpdf
1509
- else:
1510
- try:
1511
- val_reg=val_reg.append(tmpdf)
1512
- except:
1513
- val_reg=val_reg._append(tmpdf)
1514
-
1515
- #合成
1516
- val_reg['日期']=val_reg.index
1517
- ret_reg['日期']=ret_reg.index
1518
-
1519
- df_reg=val_reg.merge(ret_reg,how='inner',on=['日期','行业'])
1520
- df_reg.set_index('日期',inplace=True)
1521
-
1522
- df_reg.dropna(inplace=True)
1523
-
1524
- output=stats.linregress(df_reg[val],df_reg['ret'])
1525
- (beta,alpha,r_value,p_value,std_err)=output
1526
-
1527
- if p_value < 0.001: siglevel='***'
1528
- elif p_value < 0.01: siglevel='** '
1529
- elif p_value < 0.05: siglevel='* '
1530
- else: siglevel=' '
1531
- r_sqr=round(r_value**2,4)
1532
-
1533
- betas=pd.DataFrame(columns=('估值指标','收益率指标','行业','截距','估值系数','p值','显著性','R-sqr'))
1534
- row=pd.Series({'估值指标':val,'收益率指标':m,'行业':'全行业','截距':alpha,'估值系数':beta, \
1535
- 'p值':p_value,'显著性':siglevel,'R-sqr':r_sqr})
1536
- try:
1537
- betas=betas.append(row,ignore_index=True)
1538
- except:
1539
- betas=betas._append(row,ignore_index=True)
1540
-
1541
- industry_list=list(set(list(df_reg['行业'])))
1542
- for i in industry_list:
1543
- dftmp=df_reg[df_reg['行业']==i]
1544
-
1545
- output=stats.linregress(dftmp[val],dftmp['ret'])
1546
- (beta,alpha,r_value,p_value,std_err)=output
1547
-
1548
- if p_value < 0.001: siglevel='***'
1549
- elif p_value < 0.01: siglevel='** '
1550
- elif p_value < 0.05: siglevel='* '
1551
- else: siglevel=' '
1552
- r_sqr=round(r_value**2,4)
1553
-
1554
- row=pd.Series({'估值指标':val,'收益率指标':m,'行业':i,'截距':alpha,'估值系数':beta, \
1555
- 'p值':p_value,'显著性':siglevel,'R-sqr':r_sqr})
1556
- try:
1557
- betas=betas.append(row,ignore_index=True)
1558
- except:
1559
- betas=betas._append(row,ignore_index=True)
1560
-
1561
- if val.lower() in ['pe','pb']:
1562
- betas['估值判断']=betas['估值系数'].apply(lambda x: '可能高估' if x <0 else '可能低估')
1563
- betas.sort_values(by=['估值系数','显著性'],ascending=[False,False],inplace=True)
1564
- elif val.lower() in ['dividend']:
1565
- betas['估值判断']=betas['估值系数'].apply(lambda x: '可能高估' if x >0 else '可能低估')
1566
- betas.sort_values(by=['估值系数','显著性'],ascending=[True,False],inplace=True)
1567
-
1568
- lo_est_betas=betas[(betas['行业']=='全行业') | (betas['估值判断']=='可能低估')]
1569
-
1570
- betas.reset_index(drop=True,inplace=True)
1571
- betas.index=betas.index + 1
1572
- betas_list=betas_list+[betas]
1573
- lo_est_betas_list=lo_est_betas_list+[lo_est_betas]
1574
-
1575
- # 整理各个行业的综合评价
1576
- # 合并betas
1577
- allbetas=pd.DataFrame()
1578
- for b in betas_list:
1579
- try:
1580
- allbetas=allbetas.append(b)
1581
- except:
1582
- allbetas=allbetas._append(b)
1583
-
1584
- valtable=pd.DataFrame(columns=('行业', \
1585
- ('PE','Annual Ret'), \
1586
- ('PE','Quarterly Ret'), \
1587
- ('PE','Monthly Ret'), \
1588
- ('PB','Annual Ret'), \
1589
- ('PB','Quarterly Ret'), \
1590
- ('PB','Monthly Ret'), \
1591
- ('dividend','Annual Ret'), \
1592
- ('dividend','Quarterly Ret'), \
1593
- ('dividend','Monthly Ret'), \
1594
- ))
1595
- valtable_list=list(valtable)
1596
- valtable_list.remove('行业')
1597
- industry_names=list(set(list(allbetas['行业'])))
1598
- for i in industry_names:
1599
- pos=industry_names.index(i)
1600
- row=pd.Series({'行业':i})
1601
- try:
1602
- valtable=valtable.append(row,ignore_index=True)
1603
- except:
1604
- valtable=valtable._append(row,ignore_index=True)
1605
-
1606
- for v in valtable_list:
1607
- val,ret=v
1608
- try:
1609
- val_value=allbetas[(allbetas['估值指标']==val) & (allbetas['收益率指标']==ret) & (allbetas['行业']==i)]['估值判断'].values[0]
1610
- if val_value == '可能低估':
1611
- val_value1='低估'
1612
- else:
1613
- val_value1='高估'
1614
-
1615
- sig_value=allbetas[(allbetas['估值指标']==val) & (allbetas['收益率指标']==ret) & (allbetas['行业']==i)]['显著性'].values[0]
1616
- vsvalue=val_value1+sig_value
1617
-
1618
- valtable.at[pos,v]=vsvalue
1619
- except:
1620
- continue
1621
-
1622
- valtable.fillna('不确定',inplace=True)
1623
-
1624
- # 排序,低估在前,PE优先
1625
- fld1=valtable_list[0]
1626
- fld2=valtable_list[1]
1627
- fld3=valtable_list[2]
1628
- valtable.sort_values(by=[fld1,fld2,fld3],ascending=[True,True,True],inplace=True)
1629
- valtable.reset_index(drop=True,inplace=True)
1630
-
1631
- print("Successfully valuated",len(valtable),'industries')
1632
- print("Valuation completed by mixing PE/PB/dividend with Annual/Quarterly/Monthly")
1633
-
1634
- return valtable,betas_list
1635
-
1636
- #==============================================================================
1637
- #==============================================================================
1638
- if __name__=='__main__':
1639
- end='2022-11-22'
1640
- start=date_adjust(end,-365*5)
1641
- itype='I'
1642
- industries='all'
1643
-
1644
- lo_est_betas_list,betas_list,idfall=valuation2return_sw(start,end,valuation=valuation,return_delay=return_delay)
1645
-
1646
- def valuation2return_sw(start,end,itype='1',industries='all'):
1647
- """
1648
- 功能:测试三种估值指标对滞后一段时间收益率的影响。
1649
- 测试行业哑元变量对估值指标的调节作用,借此判断。若正向(负向)影响,行业估值未低估(高估)
1650
- start, end: 测试期间
1651
- itype: 申万指数种类,默认行业类别I, 市场表征F, 投资风格F,全部A
1652
- industries: 指定具体的指数列表,用于节省处理时间,默认all
1653
- """
1654
- #设定相关参数
1655
- # 估值指标,市盈率PE, 市净率PB, 股息率dividend,三项合用
1656
- valuation=['PE','PB','dividend']
1657
- # 估值指标对收益率影响的滞后时间长度
1658
- # Monthly一个月=21天,Quarterly一个季度=63天,Annual一年=252天。分别回归,用于观察期间长短的影响
1659
- # 用于判断过去某个时点的估值指标能够对当前的收益率产生影响,以及何种影响
1660
- return_delay=['Annual','Quarterly','Monthly']
1661
-
1662
- # 检查日期的合理性
1663
- flag,start1,end1=check_period(start,end)
1664
- if not flag:
1665
- print(" #Error(valuation2return_sw): invalid date period",start,end)
1666
- return None
1667
-
1668
- # 检查估值指标的类型
1669
- valuationlist=['pe','pb','dividend']
1670
- if isinstance(valuation,str):
1671
- valuation_list=[valuation]
1672
- elif isinstance(valuation,list):
1673
- valuation_list=valuation
1674
- for v in valuation_list:
1675
- if not (v.lower() in valuationlist):
1676
- print(" #Warning(valuation2return_sw): unsupported type of valuation:",v)
1677
- print(" supported types of valuation:",valuationlist)
1678
- return None
1679
-
1680
- # 检查估值指标的类型
1681
- return_delaylist=['annual','quarterly','monthly']
1682
- measurelist=['Annual Ret','Quarterly Ret','Monthly Ret']
1683
- shiftlist=[252,63,21]
1684
-
1685
- if isinstance(return_delay,str):
1686
- return_delay_list=[return_delay]
1687
- elif isinstance(return_delay,list):
1688
- return_delay_list=return_delay
1689
-
1690
- measure_list=[]
1691
- shift_days_list=[]
1692
- for v in return_delay_list:
1693
- if not (v.lower() in return_delaylist):
1694
- print(" #Warning(valuation2return_sw): unsupported type of return delay:",v)
1695
- print(" supported types of return delay:",return_delaylist)
1696
- return None
1697
-
1698
- pos=return_delaylist.index(v.lower())
1699
- measure=measurelist[pos]
1700
- measure_list=measure_list+[measure]
1701
- shift_days=shiftlist[pos]
1702
- shift_days_list=shift_days_list+[shift_days]
1703
-
1704
- #屏蔽函数内print信息输出的类
1705
- import os, sys
1706
- class HiddenPrints:
1707
- def __enter__(self):
1708
- self._original_stdout = sys.stdout
1709
- sys.stdout = open(os.devnull, 'w')
1710
-
1711
- def __exit__(self, exc_type, exc_val, exc_tb):
1712
- sys.stdout.close()
1713
- sys.stdout = self._original_stdout
1714
-
1715
- # 步骤1:获取行业历史数据,本步骤所需时间较长==================================
1716
- print("Step1: retrieving industry information, it may take up to hours ...")
1717
- industry_data=get_industry_sw(itype=itype)
1718
- if not (industries.lower() == 'all'):
1719
- industry_codes=industry_sw_codes(industries)
1720
- else:
1721
- industry_codes=list(set(list(industry_data['ticker'])))
1722
-
1723
- # 步骤2:计算基础数据,本步骤所需时间较长======================================
1724
- print("Step2: Calculating industry valuations, it may take great time ...")
1725
- idf,idfall=calc_industry_sw(industry_data,start,end)
1726
-
1727
- # 步骤3:构造回归数据,进行回归,记录回归结果
1728
- import pandas as pd
1729
- coefdflist=[]
1730
- print("Step3: Analyzing industry performance, it may need quite some time ...")
1731
-
1732
- total=len(measure_list)*len(valuation_list)
1733
- for m in measure_list:
1734
-
1735
- # (1)计算收益率
1736
- #print(" Processing measure",m)
1737
- ret_df=compare_industry_sw(idfall,industry_codes,measure=m,graph=False)
1738
- industry_names=list(ret_df)
1739
- pos=measure_list.index(m)
1740
- d=shift_days_list[pos]
1741
-
1742
- ret_collist=list(ret_df)
1743
- ret_reg=pd.DataFrame()
1744
- obs_num=0
1745
- # 构造用于回归的数据结构
1746
- for i in ret_collist:
1747
- tmpdf=ret_df[[i]]
1748
- tmpdf['行业']=i
1749
- tmpdf.rename(columns={i:'ret'},inplace=True)
1750
-
1751
- if len(ret_reg)==0:
1752
- ret_reg=tmpdf
1753
- else:
1754
- try:
1755
- ret_reg=ret_reg.append(tmpdf)
1756
- except:
1757
- ret_reg=ret_reg._append(tmpdf)
1758
-
1759
- # (2)处理估值指标
1760
- df=pd.DataFrame()
1761
- for val in valuation_list:
1762
-
1763
- #print(" Handling valuation",val,'with measure',m)
1764
-
1765
- # (a)计算估值指标
1766
- with HiddenPrints():
1767
- val_df=compare_industry_valuation_sw(industry_names,start=start,end=end,
1768
- vtypes=val,graph=False)
1769
- # (b)滞后估值指标
1770
- val_df2=val_df.shift(d)
1771
- val_collist=list(val_df2)
1772
- val_reg=pd.DataFrame()
1773
-
1774
- for i in val_collist:
1775
- tmpdf=val_df2[[i]]
1776
- tmpdf['行业']=i
1777
- tmpdf.rename(columns={i:val},inplace=True)
1778
-
1779
- if len(val_reg)==0:
1780
- val_reg=tmpdf
1781
- else:
1782
- try:
1783
- val_reg=val_reg.append(tmpdf)
1784
- except:
1785
- val_reg=val_reg._append(tmpdf)
1786
-
1787
- # (c)合成滞后的估值指标和收益率
1788
- val_reg['日期']=val_reg.index
1789
- ret_reg['日期']=ret_reg.index
1790
-
1791
- df_reg=val_reg.merge(ret_reg,how='inner',on=['日期','行业'])
1792
- df_reg.set_index('日期',inplace=True)
1793
- df_reg2=index2date(df_reg,date_field='date')
1794
-
1795
- df_reg2.dropna(inplace=True)
1796
-
1797
- if len(df)==0:
1798
- df=df_reg2
1799
- else:
1800
- df=pd.merge(df,df_reg2,how='outer',on=['date','行业'])
1801
-
1802
- # 本步骤所需时间漫长,显示当前进度
1803
- current=measure_list.index(m)*len(valuation_list)+valuation_list.index(val)
1804
- print_progress_percent(current,total,steps=5,leading_blanks=2)
1805
-
1806
- # (3)增加额外的自变量:PB/PE表示净资产产生利润的能力
1807
- #print("Model regression for the impact on industrial returns by prior valuation ...")
1808
- # PE*PB表示利润与净资产对于股价的联合支撑作用
1809
- df['PBdivPE']=df['PB']/df['PE']
1810
- df['PBxPE']=df['PB']*df['PE']
1811
- xList=['PE','PB','dividend','PBdivPE','PBxPE']
1812
-
1813
- # (4)构造哑元变量:行业,年度
1814
- df['year']=df['date'].apply(lambda x:x[:4])
1815
- df2,indDummies,yDummies=df_fe2(df,industry_field="行业",year_field='year')
1816
- obs_num=obs_num+len(df2)
1817
-
1818
- # (5)多元回归,记录行业哑元变量的回归系数和显著性
1819
- coefdf=multi_ols(df2,xList=xList,y='ret',industryDummies=indDummies,yearDummies=yDummies)
1820
- coefdf2=coefdf.T[indDummies].T
1821
- coefdf2['val']=coefdf2['coef'].apply(lambda x: '低估' if x >0 else '高估')
1822
- coefdf2['估值判断']=coefdf2['val']+coefdf2['sig']
1823
- coefdf2['measure']=m
1824
-
1825
- coefdflist=coefdflist+[coefdf2]
1826
-
1827
- # 步骤4:制作多行业多期的估值评价矩阵
1828
- allbetas=pd.DataFrame()
1829
- for b in coefdflist:
1830
- b['行业0']=b.index
1831
- b['行业']=b['行业0'].apply(lambda x:x[1:])
1832
- if b['measure'].values[0]=='Annual Ret':
1833
- b.rename(columns={'估值判断': '年度估值判断'}, inplace=True)
1834
- b2=b[['行业','年度估值判断']]
1835
- elif b['measure'].values[0]=='Quarterly Ret':
1836
- b.rename(columns={'估值判断': '季度估值判断'},inplace=True)
1837
- b2=b[['行业','季度估值判断']]
1838
- else:
1839
- b.rename(columns={'估值判断': '月度估值判断'},inplace=True)
1840
- b2=b[['行业', '月度估值判断']]
1841
-
1842
- if len(allbetas)==0:
1843
- allbetas=b2
1844
- else:
1845
- allbetas=pd.merge(allbetas,b2,how='outer',on=['行业'])
1846
-
1847
- allbetas.fillna('未知',inplace=True)
1848
-
1849
- allbetas['score1']=allbetas['年度估值判断'].apply(lambda x: val_score(x))
1850
- allbetas['score2']=allbetas['季度估值判断'].apply(lambda x: val_score(x))
1851
- allbetas['score3']=allbetas['月度估值判断'].apply(lambda x: val_score(x))
1852
- allbetas['score']=allbetas['score1']+allbetas['score2']+allbetas['score3']
1853
-
1854
- loest=len(allbetas[allbetas['score']<0])
1855
- hiest=len(allbetas[allbetas['score']>0])
1856
-
1857
- allbetas.sort_values(by=['score','行业'],ascending=[True,True],inplace=True)
1858
-
1859
- allbetas.reset_index(drop=True,inplace=True)
1860
- allbetas2=allbetas[['行业', '年度估值判断', '季度估值判断', '月度估值判断']]
1861
- allbetas2.index=allbetas2.index+1
1862
-
1863
- print("\nResults:")
1864
- modelstr='ret = lagged('+xList[0]
1865
- for x in xList[1:]:
1866
- modelstr=modelstr+' + '+x
1867
- modelstr=modelstr+')'+' + '+'Industry/Year dummies'
1868
- print(" Valuation model:",modelstr)
1869
- print(" Depenbdent: using Annual/Quarterly/Monthly ret respectively")
1870
-
1871
- print(" Sample period:",start,'to',end,'\b, total',obs_num,'observations for regression')
1872
-
1873
- print(" ",len(allbetas2),'industries valuated,',str(loest)+'('+str(hiest)+') might be under(over) estimated from future return perspective')
1874
-
1875
- # 在Jupyter Notebook可直接显示返回的变量,格式整齐
1876
- return allbetas2
1877
-
1878
- #==============================================================================
1879
- def val_score(val_comment):
1880
- """
1881
- 功能:基于估值判断给出评分,仅用于排序,无其他实际意义
1882
- """
1883
- #print(val_comment,len(val_comment))
1884
- if val_comment == '未知':
1885
- score=0
1886
-
1887
- if val_comment == '低估 ':
1888
- score=-1
1889
- if val_comment == '低估* ':
1890
- score=-2
1891
- if val_comment == '低估** ':
1892
- score=-3
1893
- if val_comment == '低估***':
1894
- score=-4
1895
-
1896
- if val_comment == '高估 ':
1897
- score=1
1898
- if val_comment == '高估* ':
1899
- score=2
1900
- if val_comment == '高估** ':
1901
- score=3
1902
- if val_comment == '高估***':
1903
- score=4
1904
-
1905
- return score
1906
-
1907
-
1908
-
1909
-
1910
- #==============================================================================
1911
- if __name__ == '__main__':
1912
- date_field='date'
1913
-
1914
- def index2date(df,date_field='date'):
1915
- """
1916
- 功能:从日期型df.index取出日期,类型YYYY-MM-DD,放在新字段date_field中。
1917
- """
1918
-
1919
- df[date_field+'pd']=df.index
1920
- df[date_field]=df[date_field+'pd'].apply(lambda x: x.strftime("%Y-%m-%d"))
1921
- del df[date_field+'pd']
1922
-
1923
- return df
1924
-
1925
- #==============================================================================
1926
-
1927
-
1928
-
1929
- def df_fe2(df,industry_field,year_field):
1930
- """
1931
- 功能:基于df做出industry_field和year_field的哑元变量'i'+industry_field和'y'+year_field
1932
-
1933
- """
1934
- ilist=[]
1935
- ylist=[]
1936
-
1937
- #生成行业哑元变量,全部预置为0和1
1938
- industry_list=list(set(list(df[industry_field])))
1939
- industry_list.sort(reverse=False)
1940
- for i in industry_list:
1941
- df['i'+i]=df[industry_field].apply(lambda x: 1 if x==i else 0)
1942
- ilist=ilist+['i'+i]
1943
-
1944
- #生成年度哑元变量,全部预置为0和1
1945
- year_list=list(set(list(df[year_field])))
1946
- year_list.sort(reverse=False)
1947
- for i in year_list:
1948
- df['y'+i]=df[year_field].apply(lambda x: 1 if x==i else 0)
1949
- ylist=ylist+['y'+i]
1950
-
1951
- return df,ilist,ylist
1952
-
1953
- #==============================================================================
1954
- def sig_level(p):
1955
- """
1956
- 功能:基于p值给出显著性星星个数
1957
-
1958
- """
1959
- if p >=0.05:
1960
- sig=" "
1961
- elif 0.05 > p >= 0.01:
1962
- sig='* '
1963
- elif 0.01 > p >= 0.001:
1964
- sig="** "
1965
- else:
1966
- sig="***"
1967
-
1968
- return sig
1969
-
1970
- #==============================================================================
1971
-
1972
- def multi_ols(df,xList,y,industryDummies,yearDummies):
1973
- """
1974
- 功能:多元线性回归, y=f(X),需要系数和显著性
1975
- df: 所有数据
1976
- xList: 自变量列表,不包括行业和年度哑元变量
1977
- y: 因变量
1978
- industryDummies: 行业哑元变量列表
1979
- yearDummies: 年度哑元变量列表
1980
- """
1981
- import statsmodels.formula.api as smf
1982
-
1983
- #构造模型表达式:y~x1+x2+.....
1984
- model=''
1985
- allXVars=xList+industryDummies+yearDummies
1986
- for x in allXVars:
1987
- if model == '':
1988
- model=y+'~'+x
1989
- else:
1990
- model=model+'+'+x
1991
-
1992
- #形成smf模型
1993
- reg = smf.ols(formula=model,data=df)
1994
-
1995
- #线性回归
1996
- result=reg.fit()
1997
- #print(result.summary())
1998
-
1999
- #自变量各个系数及其显著性
2000
- import pandas as pd
2001
- coefMatrix=pd.DataFrame([result.params,result.pvalues],index=["coef","p"]).T
2002
-
2003
- coefMatrix['sig']=coefMatrix['p'].apply(lambda x: sig_level(x))
2004
-
2005
- #整个模型的显著性:F-test
2006
- modelSig=result.f_pvalue
2007
-
2008
- return coefMatrix
2009
-
2010
- #==============================================================================
2011
- #==============================================================================
2012
- #==============================================================================
2013
- if __name__=='__main__':
2014
- sw_code='850831.SW'
2015
- sw_code='801193.SW'
2016
- indicator='PE'
2017
- start='2023-1-1'
2018
- end='2023-12-15'
2019
- top=10
2020
-
2021
- def valuation_industry_sw_generating(sw_code,indicator,start,end,top=5):
2022
- """
2023
- 功能:模拟申万行业指数的估值,PE/PB/股息率等
2024
- sw_code:申万行业分类指数,各个级别
2025
- start/end:开始/结束日期
2026
- top:使用前几大成分股的估值进行合成
2027
-
2028
- 注意:指数模拟出的估值曲线波动过大,缺乏实用价值!
2029
- """
2030
- import pandas as pd
2031
- #查找申万行业指数成分股
2032
- clist,cdf=industry_stock_sw(industry=sw_code,top=top)
2033
-
2034
- #查找成分股的历史估值
2035
- df=None
2036
- for t in clist:
2037
- dft=get_stock_valuation_cn_hk(ticker=t,indicators=indicator,start=start,end=end)
2038
- dft[t]=dft[indicator]
2039
- dft2=dft[[t]]
2040
- if dft2 is None: continue
2041
-
2042
- #将负数填充为0,不计入估值?整个成分股剔除?
2043
- dft2[t]=dft2[t].apply(lambda x: 0 if x<0 else x)
2044
-
2045
- if df is None:
2046
- df=dft2
2047
- else:
2048
- df=pd.merge(df,dft2,how='outer',left_index=True,right_index=True)
2049
-
2050
- #成分股权重
2051
- weight=list(cdf['最新权重'])
2052
-
2053
- #各行权重分别求和
2054
- dfw=df.copy()
2055
- collist=list(dfw)
2056
- for c in collist:
2057
- dfw[c]=dfw[c].apply(lambda x: 0 if x<=0 else 1)
2058
- dfw['weight']=dfw.dot(weight)
2059
- dfw2=dfw[['weight']]
2060
-
2061
- #加权平均
2062
- df['weighted_total']=df.dot(weight)
2063
- df2=pd.merge(df,dfw2,left_index=True,right_index=True)
2064
-
2065
- df2['weighted_avg']=df2['weighted_total']/df2['weight']
2066
- df2['code']=sw_code
2067
- df3=df2[['code','weighted_avg']]
2068
-
2069
- #因有市盈率负数,不管如何处理都会导致加权平均后数值波动过大,不能实用
2070
- return df3
2071
-
2072
-
2073
-
2074
-
2075
-
2076
- #==============================================================================