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,1576 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- 本模块功能:投资组合的风险调整收益率教学插件(算法II)
4
- 所属工具包:证券投资分析工具SIAT
5
- SIAT:Security Investment Analysis Tool
6
- 创建日期:2018年10月16日
7
- 最新修订日期:2025年6月20日
8
- 作者:王德宏 (WANG Dehong, Peter)
9
- 作者单位:北京外国语大学国际商学院
10
- 作者邮件:wdehong2000@163.com
11
- 版权所有:王德宏
12
- 用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
13
- 特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
14
- """
15
-
16
- #==============================================================================
17
- #关闭所有警告
18
- import warnings; warnings.filterwarnings('ignore')
19
- #==============================================================================
20
- from siat.common import *
21
- from siat.translate import *
22
- from siat.security_prices import *
23
- from siat.security_price2 import *
24
- from siat.fama_french import *
25
- from siat.grafix import *
26
-
27
- import pandas as pd
28
- #==============================================================================
29
- import matplotlib.pyplot as plt
30
-
31
- #处理绘图汉字乱码问题
32
- import sys; czxt=sys.platform
33
- if czxt in ['win32','win64']:
34
- plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
35
- mpfrc={'font.family': 'SimHei'}
36
-
37
- if czxt in ['darwin']: #MacOSX
38
- plt.rcParams['font.family']= ['Heiti TC']
39
- mpfrc={'font.family': 'Heiti TC'}
40
-
41
- if czxt in ['linux']: #website Jupyter
42
- plt.rcParams['font.family']= ['Heiti TC']
43
- mpfrc={'font.family':'Heiti TC'}
44
-
45
- # 解决保存图像时'-'显示为方块的问题
46
- plt.rcParams['axes.unicode_minus'] = False
47
- #==============================================================================
48
- #==============================================================================
49
- #==============================================================================
50
- def calc_treynor_ratio(regdf):
51
- """
52
- 功能:计算一项特雷诺指数
53
- 输入:数据框,至少含有Ret-Rf和Mkt-Rf两项
54
- 输出:特雷诺指数,Ret-Rf均值
55
- """
56
-
57
- #计算风险溢价Ret-RF均值
58
- ret_rf_mean=regdf['Ret-RF'].mean()
59
-
60
- #使用CAPM回归计算投资组合的贝塔系数,这里得到的alpha就是Jensen's alpha
61
- from scipy import stats
62
- output=stats.linregress(regdf['Mkt-RF'],regdf['Ret-RF'])
63
- (beta,alpha,r_value,p_value,std_err)=output
64
-
65
- #计算特雷诺指数
66
- tr=ret_rf_mean/beta
67
-
68
- #ret_mean=regdf['Ret%'].mean()
69
- rp_mean=ret_rf_mean
70
- return tr,rp_mean,beta
71
-
72
- #==============================================================================
73
- def calc_alpha_ratio(regdf):
74
- """
75
- 功能:计算一项詹森阿尔法指数
76
- 输入:数据框,至少含有Ret-Rf和Mkt-Rf两项
77
- 输出:詹森阿尔法指数,Ret-Rf均值
78
- """
79
- #计算风险溢价Ret-RF均值
80
- ret_rf_mean=regdf['Ret-RF'].mean()
81
- #使用CAPM回归计算投资组合的贝塔系数,这里得到的alpha就是Jensen's alpha
82
- from scipy import stats
83
- output=stats.linregress(regdf['Mkt-RF'],regdf['Ret-RF'])
84
- (beta,alpha,r_value,p_value,std_err)=output
85
-
86
- rp_mean=ret_rf_mean
87
- return alpha,rp_mean,beta
88
-
89
- #==============================================================================
90
- def calc_sharpe_ratio(regdf):
91
- """
92
- 功能:计算一项夏普指数
93
- 输入:数据框,至少含有Ret-Rf和Mkt-Rf两项
94
- 输出:夏普指数,Ret-Rf均值
95
- """
96
- #计算风险溢价Ret-RF均值和标准差
97
- ret_rf_mean=regdf['Ret-RF'].mean()
98
- ret_rf_std=regdf['Ret-RF'].std()
99
-
100
- #计算夏普指数
101
- sr=ret_rf_mean/ret_rf_std
102
-
103
- rp_mean=ret_rf_mean
104
- beta=False
105
- return sr,rp_mean,beta
106
-
107
- if __name__=='__main__':
108
- rfd=rf_daily_china('2021-10-1','2021-11-28',rate_period='1Y',rate_type='shibor')
109
- rfd=rf_daily_china('2021-11-1','2021-11-28',rate_period='3M',rate_type='shibor')
110
-
111
- prices=get_prices('837344.BJ','2021-11-1','2021-11-28')
112
- prices['ret_daily']=prices['Close'].pct_change()
113
- rp=pd.merge(prices,rfd,how='left',left_index=True,right_index=True)
114
- rp['r-rf']=rp['ret_daily']-rp['rf_daily']
115
- rp.dropna(inplace=True)
116
- sharpe1=rp['r-rf'].mean()/rp['r-rf'].std()
117
- sharpe2=rp['ret_daily'].mean()/rp['ret_daily'].std()
118
- #==============================================================================
119
- def calc_sortino_ratio(regdf):
120
- """
121
- 功能:计算一项索替诺指数
122
- 输入:数据框,至少含有Ret-Rf和Mkt-Rf两项
123
- 输出:索替诺指数,Ret-Rf均值
124
- """
125
-
126
- #计算风险溢价Ret-RF均值和下偏标准差LPSD
127
- ret_rf_mean=regdf['Ret-RF'].mean()
128
- reg2=regdf[regdf['Ret-RF'] < 0]
129
- ret_rf_lpsd=reg2['Ret-RF'].std()
130
-
131
- #计算索梯诺指数
132
- sr=ret_rf_mean/ret_rf_lpsd
133
-
134
- rp_mean=ret_rf_mean
135
- beta=False
136
- return sr,rp_mean,beta
137
-
138
- #==============================================================================
139
- def print_rar_ratio(regdf,portfolio,rp_mean,beta,ratio_name,ratio):
140
- """
141
- 功能:打印风险调整后的收益率
142
- 输入:数据框,投资组合构成,收益溢价均值,贝塔系数,指数名称,指数
143
- 输出:打印
144
-
145
- 注意:若贝塔系数为False则不打印
146
- """
147
-
148
- #从字典中提取信息
149
- scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
150
- stocklist1,_=cvt_yftickerlist(stocklist)
151
-
152
- date_start=str(regdf.index[0].year)+'-'+str(regdf.index[0].month)+ \
153
- '-'+str(regdf.index[0].day)
154
- date_end=str(regdf.index[-1].year)+'-'+str(regdf.index[-1].month)+ \
155
- '-'+str(regdf.index[-1].day)
156
- print("\n======== 风险调整收益率 ========")
157
- print("证券资产:",portfolio_name(portfolio))
158
- #print("市场指数:",ectranslate(scope),'\b,',ticker_name(mktidx))
159
- print("市场指数:",ticker_name(mktidx))
160
- #print("成分股 :",ticker_name(stocklist))
161
- #print("持仓权重:",portionlist)
162
- print("样本期间:",date_start,"至",date_end)
163
- """
164
- print("日均收益率:",round(ret_mean,4),'\b%')
165
- annual_ret=(1+ret_mean/100)**252-1
166
- print("年化收益率:",round(annual_ret,4))
167
- """
168
- if not isinstance(beta,bool):
169
- print("贝塔系数:",round(beta,4))
170
-
171
- print("风险溢价均值%:",round(rp_mean,4))
172
-
173
- #print(ratio_name.capitalize(),"\b比率:",round(ratio,4),'\b%')
174
- print(ratio_name.capitalize(),"\b比率%:",round(ratio,4))
175
- """
176
- print("***投资组合构成:")
177
- print_tickerlist_sharelist(stocklist,portionlist,2)
178
- """
179
-
180
- import datetime as dt; todaydt=dt.date.today()
181
- print("数据来源:新浪/stooq, "+str(todaydt))
182
-
183
- return
184
- #==============================================================================
185
- if __name__=='__main__':
186
- portfolio={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
187
- start='2024-6-1'
188
- end='2025-5-30'
189
- RF=0.04
190
- printout=True
191
-
192
- rate_period='ON'
193
-
194
- def treynor_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
195
- """
196
- 功能:按天计算一个投资组合的特雷诺指数
197
- 投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
198
- 输入:投资组合,开始日期,结束日期
199
- 输出:特雷诺指数
200
- """
201
-
202
- #第1步:各种准备和检查工作
203
- #设定错误信息的函数名
204
- func_name='treynor_ratio_portfolio'
205
- #设定需要计算的指数名称
206
- ratio_name="treynor"
207
- result,startdate,enddate=check_period(start,end)
208
- if not result:
209
- message=" #Error("+func_name+"): "+"invalid start or end date:"
210
- print(message,start,end)
211
- return None,None
212
-
213
- #从字典中提取信息
214
- scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
215
-
216
- #第2步:获得无风险收益率/市场收益率序列
217
- #获得期间的日无风险收益率(抓取的RF为百分比)
218
- if isinstance(RF,bool):
219
- print(" Searching for risk-free interest rate ...")
220
- if scope=='China':
221
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
222
- else:
223
- rf_df=get_rf(start,end,scope=scope,freq='daily')
224
- if rf_df is None:
225
- message=" #Error("+func_name+"): "+"no data available for rf in"
226
- print(message,scope,start,end)
227
- return None,None
228
- RF=rf_df['RF'].mean()
229
-
230
- #第3步:计算投资组合的日收益率序列
231
- #抓取日投资组合价格:内含Mkt-RF和RF
232
- sp=get_portfolio_prices(portfolio,start,end,RF=RF)
233
- #计算日收益率,表示为百分比
234
- """
235
- import pandas as pd
236
- ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
237
- ret_pf=ret_pf.dropna()
238
- """
239
- ret_pf=sp
240
-
241
- #第4步:合并投资组合日收益率与无风险利率/市场收益率序列
242
- """
243
- if isinstance(RF,bool):
244
- #合并rf_df与ret_pf
245
- reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
246
- else:
247
- reg=ret_pf
248
- reg['RF']=RF/365 #日度无风险收益率%
249
- reg['Ret-RF']=reg['Close']-reg['RF']
250
- """
251
- reg=ret_pf
252
- reg=reg.dropna()
253
- if len(reg) == 0:
254
- message=" #Error("+func_name+"): "+"empty ret-rf data for regression"
255
- print(message)
256
- return None,None
257
-
258
- #第5步:计算风险调整后的收益率
259
- ##########风险调整后的收益率,计算开始##########
260
- tr,rp_mean,beta=calc_treynor_ratio(reg)
261
- ##########风险调整后的收益率,计算结束##########
262
-
263
- #第6步:打印结果
264
- if printout == True:
265
- print_rar_ratio(reg,portfolio,rp_mean,beta,ratio_name,tr)
266
-
267
- return tr,rp_mean
268
-
269
-
270
- if __name__=='__main__':
271
- portfolio1={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
272
- tr1,ret1=treynor_ratio_portfolio(portfolio1,'2019-01-01','2019-01-31')
273
-
274
-
275
- #==============================================================================
276
- if __name__=='__main__':
277
- portfolio={'Market':('US','^GSPC'),'EDU':0.6,'TAL':0.4}
278
- start='2025-1-01'
279
- end ='2025-5-30'
280
- RF=0.04; printout=True
281
- indicator='sharpe'
282
- indicator='alpha'
283
-
284
-
285
- def rar_ratio_portfolio(portfolio,start='MRY',end='today', \
286
- indicator='sharpe', \
287
- RF=0,printout=True):
288
- """
289
- 功能:按天计算一个投资组合的风险调整后的收益率指数
290
- 投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
291
- 输入:投资组合,开始日期,结束日期,rar种类
292
- 输出:风险调整后的收益率指数
293
- """
294
- ratio_name=indicator
295
-
296
- #第1步:各种准备和检查工作
297
- #设定错误信息的函数名
298
- func_name='rar_ratio_portfolio'
299
-
300
- ratio_name=ratio_name.lower()
301
- ratio_list=['treynor','sharpe','sortino','alpha']
302
- if ratio_name not in ratio_list:
303
- message=" #Error("+func_name+"): "+"unsupported rar ratio type"
304
- print(message)
305
- return None,None
306
-
307
- start,end=start_end_preprocess(start,end)
308
- result,startdate,enddate=check_period(start,end)
309
- if not result:
310
- message=" #Error("+func_name+"): "+"invalid start or end date"
311
- print(message,start,end)
312
- return None,None
313
-
314
- print(f" Calculating {ratio_name} ratio ...")
315
- #从字典中提取信息
316
- scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
317
-
318
- #第2步:获得无风险收益率/市场收益率序列
319
- #获得期间的日无风险收益率(抓取的RF为百分比)
320
- rf_value_flag=True #RF以数值形式给出
321
- if isinstance(RF,bool):
322
- rf_value_flag=False
323
- if RF:
324
- print(" Searching for risk-free interest rate ...")
325
- if scope=='China':
326
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
327
- else:
328
- rf_df=get_rf(start,end,scope=scope,freq='daily')
329
- if rf_df is None:
330
- message=" #Error("+func_name+"): "+"no data available for rf in"
331
- print(message,scope,start,end)
332
- return None,None
333
- RF=rf_df['RF'].mean()
334
- else:
335
- RF=0
336
- rf_value_flag=True
337
-
338
- #第3步:计算投资组合的日收益率序列
339
- import os,sys
340
- class HiddenPrints:
341
- def __enter__(self):
342
- self._original_stdout = sys.stdout
343
- sys.stdout = open(os.devnull, 'w')
344
-
345
- def __exit__(self, exc_type, exc_val, exc_tb):
346
- sys.stdout.close()
347
- sys.stdout = self._original_stdout
348
-
349
- #抓取日投资组合价格
350
- with HiddenPrints():
351
- sp=get_portfolio_prices(portfolio,startdate,enddate,RF=RF)
352
- if sp is None:
353
- print(" #Error(rar_ratio_portfolio): failed to retrieve portfolio information")
354
- return None,None
355
- if len(sp) == 0:
356
- print(" #Error(rar_ratio_portfolio): no portfolio information found during the period")
357
- return None,None
358
- """
359
- #计算日收益率,表示为百分比
360
- import pandas as pd
361
- ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
362
- ret_pf=ret_pf.dropna()
363
-
364
- #第4步:合并投资组合日收益率与无风险利率/市场收益率序列
365
- if not rf_value_flag:
366
- #合并rf_df与ret_pf
367
- reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
368
-
369
- else:
370
- ret_pf['RF']=RF
371
- reg=ret_pf
372
-
373
- reg['Ret-RF']=reg['Close']-reg['RF']
374
- """
375
- reg=sp
376
- reg=reg.dropna()
377
- if len(reg) == 0:
378
- message=" #Error("+func_name+"): "+"empty data for ratio calculation"
379
- print(message)
380
- return None,None
381
-
382
- #第4步:计算风险调整后的收益率
383
- ##########风险调整后的收益率,计算开始##########
384
- calc_func='calc_'+ratio_name+'_ratio'
385
- rar,rp_mean,beta=eval(calc_func)(reg)
386
- ##########风险调整后的收益率,计算结束##########
387
-
388
- #第5步:打印结果
389
- if printout == True:
390
- print_rar_ratio(reg,portfolio,rp_mean,beta,ratio_name,rar)
391
-
392
- return rar,rp_mean
393
-
394
-
395
- if __name__=='__main__':
396
- pf1={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
397
- tr1,rp1=rar_ratio_portfolio(pf1,'2019-01-01','2019-01-31',ratio_name='treynor')
398
-
399
- #==============================================================================
400
- if __name__=='__main__':
401
- portfolio={'Market':('US','^GSPC'),'AAPL':1}
402
- start='2019-12-1'
403
- end ='2021-1-31'
404
- scope='US'
405
- ratio_name='sharpe'
406
- window=30
407
- graph=True
408
-
409
- def rar_ratio_rolling(portfolio,start='MRY',end='today',indicator='sharpe',RF=0, \
410
- window=21,graph=True,source='auto'):
411
- """
412
- 功能:滚动计算一个投资组合的风险调整后的收益率指数
413
- 投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
414
- 输入:投资组合,开始日期,结束日期,指数名称,滚动窗口宽度(天数)
415
- 输出:风险调整后的收益率指数序列
416
- 注意:因需要滚动计算,开始和结束日期之间需要拉开距离,提前的月数为window/21取整+1;
417
- 另外,无风率可用数据可能距离当前日期滞后约两个月
418
-
419
- 注意:当RF=False时有bug
420
- """
421
- start,end=start_end_preprocess(start,end)
422
-
423
- ratio_name=indicator.lower()
424
-
425
- #第1步:各种准备和检查工作
426
- print(" Start to calculate rar ratios, please wait ...")
427
- #设定错误信息的函数名
428
- func_name='rar_ratio_portfolio'
429
-
430
- ratio_list=['treynor','sharpe','sortino','alpha']
431
- if ratio_name not in ratio_list:
432
- message=" #Error("+func_name+"): "+"unsupported rar ratio type"
433
- print(message,ratio_name)
434
- return None
435
-
436
- result,startdate,enddate=check_period(start,end)
437
- if not result:
438
- message=" #Error("+func_name+"): "+"invalid start or end date"
439
- print(message,start,end)
440
- return None
441
- #估算数据提前量,重设开始日历日期
442
- #startdate_delta=int(window/20*30)+30
443
- startdate_delta=int(window/20*31)
444
- startdate1=date_adjust(startdate, adjust=-startdate_delta)
445
-
446
- #从字典中提取信息
447
- scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
448
- pname=portfolio_name(portfolio)
449
- if pname == '': pname="投资组合"
450
-
451
- #第2步:获得无风险收益率/市场收益率序列
452
- #获得期间的日无风险收益率(抓取的RF为百分比)
453
- rf_value_flag=True #RF以数值形式给出
454
- if isinstance(RF,bool):
455
- rf_value_flag=False
456
- if RF:
457
- print(" Searching for risk-free interest rate ...")
458
- if scope=='China':
459
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
460
- else:
461
- rf_df=get_rf(start,end,scope=scope,freq='daily')
462
- if rf_df is None:
463
- message=" #Error("+func_name+"): "+"no data available for rf in"
464
- print(message,scope,start,end)
465
- return None,None
466
- RF=rf_df['RF'].mean()
467
- else:
468
- RF=0
469
- rf_value_flag=True
470
-
471
- #第3步:计算投资组合的日收益率序列
472
- #抓取日投资组合价格
473
- sp=get_portfolio_prices(portfolio,startdate1,enddate,RF=RF)
474
- if sp is None:
475
- print(" #Error(rar_ratio_portfolio): failed to retrieve portfolio information")
476
- return None,None
477
- if len(sp) == 0:
478
- print(" #Error(rar_ratio_portfolio): no portfolio information found during the period")
479
- return None,None
480
- """
481
- #计算日收益率,表示为百分比
482
- import pandas as pd
483
- ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
484
- ret_pf=ret_pf.dropna()
485
-
486
- #第4步:合并投资组合日收益率与无风险利率/市场收益率序列
487
- if not rf_value_flag:
488
- #合并rf_df与ret_pf
489
- reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
490
-
491
- else:
492
- ret_pf['RF']=RF
493
- reg=ret_pf
494
-
495
- reg['Ret-RF']=reg['Close']-reg['RF']
496
- """
497
- reg=sp
498
- reg=reg.dropna()
499
- if len(reg) == 0:
500
- message=" #Error("+func_name+"): "+"empty data for ratio calculation"
501
- print(message)
502
- return None,None
503
-
504
- #第4步:滚动计算风险调整后的收益率
505
- ##########风险调整后的收益率,计算开始##########
506
- #用于保存rar和ret_rf_mean
507
- import pandas as pd
508
- import numpy as np
509
- datelist=reg.index.to_list()
510
- calc_func='calc_'+ratio_name+'_ratio'
511
-
512
- rars=pd.DataFrame(columns=('Date','RAR','Mean(Ret)'))
513
- for i in np.arange(0,len(reg)):
514
- i1=i+window-1
515
- if i1 >= len(reg): break
516
-
517
- #构造滚动窗口
518
- windf=reg[reg.index >= datelist[i]]
519
- windf=windf[windf.index <= datelist[i1]]
520
- #print(i,datelist[i],i1,datelist[i1],len(windf))
521
-
522
- #使用滚动窗口计算
523
- try:
524
- rar,ret_mean=eval(calc_func)(windf)
525
- except:
526
- print(" #Error(rar_ratio_rolling): failed in linear regression for",calc_func)
527
- #print(" windf:\n",windf)
528
- continue
529
-
530
- #记录计算结果
531
- row=pd.Series({'Date':datelist[i1],'RAR':rar,'Mean(Ret)':ret_mean})
532
- try:
533
- rars=rars.append(row,ignore_index=True)
534
- except:
535
- # 可能与Python 3.11有关,不确定
536
- rars=rars._append(row,ignore_index=True)
537
-
538
- rars.set_index(['Date'],inplace=True)
539
- ##########风险调整后的收益率,计算结束##########
540
-
541
- #第5步:绘图
542
- if graph == True:
543
- print(" Rendering graphics ...")
544
- draw_rar_ratio(rars,portfolio,ratio_name,pname)
545
-
546
- return rars
547
-
548
-
549
- if __name__=='__main__':
550
- pf1={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
551
- rars1=rar_ratio_rolling(pf1,'2020-1-1','2020-12-31',ratio_name='sharpe')
552
- #==============================================================================
553
- def draw_rar_ratio(rars,portfolio,ratio_name,pname):
554
- """
555
- 功能:绘制滚动窗口曲线
556
- 输入:滚动数据df,投资组合,指数名称
557
- """
558
-
559
- scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
560
- stocklist1,_=cvt_yftickerlist(stocklist)
561
-
562
- """
563
- #平滑处理
564
- rars1=rars.resample('H')
565
- #rars2=rars1.interpolate(method='pchip')
566
- #rars2=rars1.interpolate(method='akima')
567
- rars2=rars1.interpolate(method='cubic')
568
- """
569
-
570
- #plt.figure(figsize=(12.8,6.4))
571
-
572
- labeltxt=ratio_name.capitalize()+'指标'
573
- plt.plot(rars['RAR'],label=labeltxt,color='red',lw=1)
574
- #plt.plot(rars['Mean(Ret)'],label='Stock(s) return(%)',color='blue',lw=1)
575
- plt.axhline(y=0.0,color='black',linestyle=':')
576
- """
577
- titletxt='风险调整收益的滚动趋势'+'\n'+str(ticker_name(stocklist))
578
- if len(stocklist) > 1:
579
- titletxt=titletxt+'\n持仓比例: '+str(portionlist)
580
- """
581
- titletxt='风险调整收益的滚动趋势:'+pname
582
- """
583
- if len(stocklist) == 1:
584
- titletxt='风险调整收益的滚动趋势'+'\n('+ticker_name(stocklist)+')'
585
- """
586
- #plt.title(titletxt,fontsize=12,fontweight='bold')
587
- plt.title(titletxt,fontsize=12)
588
-
589
- #ylabeltxt="比率/指数"
590
- #plt.ylabel(ylabeltxt,fontsize=12)
591
- #plt.xticks(rotation=45,fontsize=9)
592
- plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
593
- plt.gca().set_facecolor('whitesmoke')
594
- #plt.xticks(rotation=30,fontsize=8)
595
- plt.legend(loc='best')
596
-
597
- import datetime as dt; today=dt.date.today()
598
- footnote="数据来源:新浪/stooq/FRED,"+str(today)
599
- plt.xlabel(footnote)
600
- plt.show()
601
-
602
- #使用seaborn绘图
603
- """
604
- import seaborn as sns
605
- with sns.axes_style("whitegrid"):
606
- fig, ax = plt.subplots(figsize=(12.8,6.4))
607
- ax.plot(rars['RAR'],label=labeltxt,color='red',lw=3)
608
- #ax.plot(rars['Mean(Ret)'],label='Stock(s) return(%)',color='blue',lw=1)
609
- plt.axhline(y=0.0,label='Zero return',color='black',linestyle=':')
610
- ax.set_title(titletxt)
611
- #ax.set_ylabel(ylabeltxt)
612
- plt.xticks(rotation=45)
613
- ax.legend(loc='best')
614
- ax.set_ylim([1.2*(rars['RAR'].min()), 1.1*(rars['RAR'].max())])
615
- """
616
- return
617
- #==============================================================================
618
- if __name__=='__main__':
619
- portfolio={'Market':('US','^GSPC'),'AAPL':0.2,'MSFT':0.6,'IBM':0.2}
620
- start='2019-01-01'
621
- end ='2019-01-31'
622
-
623
-
624
- def sharpe_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
625
- """
626
- 功能:按天计算一个投资组合的夏普指数
627
- 投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
628
- 输入:投资组合,开始日期,结束日期
629
- 输出:夏普指数
630
- """
631
- #设定错误信息的函数名
632
- func_name='sharpe_ratio_portfolio'
633
-
634
- #检查日期期间的合理性
635
- valid,start1,end1=check_period(start,end)
636
- if not valid:
637
- print(" #Error(sharpe_ratio_portfolio): incorrect start/end date(s)",start,end)
638
- return None,None
639
-
640
- #从字典中提取信息
641
- scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
642
-
643
- #检查份额配比是否合理
644
- """
645
- if round(sum(portionlist),1) != 1.0:
646
- print(" #Error(sharpe_ratio_portfolio): Incorrect total of portions")
647
- return None,None
648
- """
649
-
650
- #获得期间的无风险收益率
651
- if isinstance(RF,bool):
652
- print(" Searching for risk-free interest rate ...")
653
- if scope=='China':
654
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
655
- else:
656
- rf_df=get_rf(start,end,scope=scope,freq='daily')
657
- if rf_df is None:
658
- message=" #Error("+func_name+"): "+"no data available for rf in"
659
- print(message,scope,start,end)
660
- return None,None
661
- RF=rf_df['RF'].mean()
662
-
663
- #抓取日投资组合价格:内含Mkt-RF和RF
664
- sp=get_portfolio_prices(portfolio,start,end,RF=RF)
665
- #计算日收益率,表示为百分比
666
- """
667
- import pandas as pd
668
- ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
669
- """
670
- ret_pf=sp
671
- ret_pf=ret_pf.dropna()
672
-
673
- #强制转换索引格式,彻底消除下面并表的潜在隐患
674
- """
675
- rf_df['ffdate']=rf_df.index.astype('str')
676
- rf_df['ffdate']=pd.to_datetime(rf_df['ffdate'])
677
- rf_df.set_index(['ffdate'],inplace=True)
678
- """
679
- """
680
- #合并rf_df与ret_pf
681
- reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
682
- reg['Ret-RF']=reg['Close']-reg['RF']
683
- reg=reg.dropna()
684
- """
685
- #计算风险溢价Ret-RF均值和标准差
686
- reg=ret_pf
687
- ret_rf_mean=reg['Ret-RF'].mean()
688
- ret_rf_std=reg['Ret-RF'].std()
689
-
690
- #计算夏普指数
691
- sr=ret_rf_mean/ret_rf_std
692
-
693
- #打印报告
694
- if printout == True:
695
- date_start=str(reg.index[0].year)+'-'+str(reg.index[0].month)+ \
696
- '-'+str(reg.index[0].day)
697
- date_end=str(reg.index[-1].year)+'-'+str(reg.index[-1].month)+ \
698
- '-'+str(reg.index[-1].day)
699
- print("\n===== 风险调整收益率 =====")
700
- """
701
- _,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
702
- if len(tickerlist)==1:
703
- product=str(ticker_name(tickerlist,'bond'))
704
- else:
705
- product=str(ticker_name(tickerlist,'bond'))+' by '+str(sharelist)
706
- """
707
- print("证券资产:",portfolio_name(portfolio))
708
- print("样本期间:",date_start,"至",date_end,"(可用日期)")
709
- print("风险溢价均值%:",round(ret_rf_mean,4))
710
- print("风险溢价标准差%:",round(ret_rf_std,4))
711
- print("夏普比率%:",round(sr,4))
712
- import datetime as dt; today=dt.date.today()
713
- print("*数据来源:新浪/stooq/FRED,"+str(today))
714
-
715
- beta=False
716
- return sr,ret_rf_mean,beta
717
-
718
-
719
- if __name__=='__main__':
720
- portfolio={'Market':('US','^GSPC'),'AAPL':0.2,'MSFT':0.5,'IBM':0.3}
721
- sr1,rp1=sharpe_ratio_portfolio(portfolio,'2019-01-01','2019-01-31')
722
-
723
-
724
- #==============================================================================
725
- if __name__=='__main__':
726
- portfolio={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
727
- start='2019-01-01'
728
- end ='2019-01-31'
729
-
730
- def sortino_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
731
- """
732
- 功能:按天计算一个投资组合的索梯诺指数
733
- 投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
734
- 输入:投资组合,开始日期,结束日期
735
- 输出:索梯诺指数
736
- """
737
- #设定错误信息的函数名
738
- func_name='sortino_ratio_portfolio'
739
-
740
- #检查日期期间的合理性
741
- valid,start1,end1=check_period(start,end)
742
- if not valid:
743
- print(" #Error(sortino_ratio_portfolio): incorrect start/end date(s)")
744
- return None,None
745
-
746
- #从字典中提取信息
747
- scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
748
-
749
- #检查份额配比是否合理
750
- """
751
- if round(sum(portionlist),1) != 1.0:
752
- print(" #Error(sortino_ratio_portfolio): Incorrect total of portions")
753
- return None,None
754
- """
755
-
756
- #获得期间的无风险收益率
757
- if isinstance(RF,bool):
758
- print(" Searching for risk-free interest rate ...")
759
- if scope=='China':
760
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
761
- else:
762
- rf_df=get_rf(start,end,scope=scope,freq='daily')
763
- if rf_df is None:
764
- message=" #Error("+func_name+"): "+"no data available for rf in"
765
- print(message,scope,start,end)
766
- return None,None
767
- RF=rf_df['RF'].mean()
768
-
769
- #抓取日投资组合价格
770
- sp=get_portfolio_prices(portfolio,start,end,RF=RF)
771
- ret_pf=sp
772
- """
773
- #计算日收益率,表示为百分比
774
- import pandas as pd
775
- ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
776
- ret_pf=ret_pf.dropna()
777
-
778
- #强制转换索引格式,彻底消除下面并表的潜在隐患
779
- rf_df['ffdate']=rf_df.index.astype('str')
780
- rf_df['ffdate']=pd.to_datetime(rf_df['ffdate'])
781
- rf_df.set_index(['ffdate'],inplace=True)
782
-
783
- #合并rf_df与ret_pf
784
- reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
785
- reg['Ret-RF']=reg['Close']-reg['RF']
786
- """
787
- reg=ret_pf
788
- reg=reg.dropna()
789
-
790
- #计算风险溢价Ret-RF均值和下偏标准差LPSD
791
- ret_rf_mean=reg['Ret-RF'].mean()
792
- reg2=reg[reg['Ret-RF'] < 0]
793
- ret_rf_lpsd=reg2['Ret-RF'].std()
794
-
795
- #计算索梯诺指数
796
- sr=ret_rf_mean/ret_rf_lpsd
797
-
798
- #打印报告
799
- if printout == True:
800
- date_start=str(reg.index[0].year)+'-'+str(reg.index[0].month)+ \
801
- '-'+str(reg.index[0].day)
802
- date_end=str(reg.index[-1].year)+'-'+str(reg.index[-1].month)+ \
803
- '-'+str(reg.index[-1].day)
804
- print("\n===== 风险调整收益率 =====")
805
- """
806
- _,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
807
- if len(tickerlist)==1:
808
- product=str(ticker_name(tickerlist,'bond'))
809
- else:
810
- product=str(ticker_name(tickerlist,'bond'))+' by '+str(sharelist)
811
- """
812
- print("证券资产:",portfolio_name(portfolio))
813
- print("样本期间:",date_start,"至",date_end,"(可用日期)")
814
- print("风险溢价均值%:",round(ret_rf_mean,4))
815
- print("下偏标准差%:",round(ret_rf_lpsd,4))
816
- print("索替诺比率%:",round(sr,4))
817
-
818
- import datetime as dt; today=dt.date.today()
819
- print("*数据来源:新浪/stooq/FRED,"+str(today))
820
-
821
- return sr,ret_rf_mean
822
-
823
-
824
- if __name__=='__main__':
825
- portfolio={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
826
- sr1,rp1=sortino_ratio_portfolio(portfolio,'2019-01-01','2019-08-03')
827
-
828
- #==============================================================================
829
- if __name__=='__main__':
830
- portfolio={'Market':('China','000001.SS'),'600519.SS':1.0}
831
- start='2019-01-01'
832
- end ='2019-01-31'
833
-
834
-
835
- def jensen_alpha_portfolio(portfolio,start,end,RF=True,printout=True):
836
- """
837
- 功能:按天计算一个投资组合的阿尔法指数
838
- 投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
839
- 输入:投资组合,开始日期,结束日期
840
- 输出:阿尔法指数
841
- """
842
- #设定错误信息的函数名
843
- func_name='jensen_alpha_portfolio'
844
-
845
- #检查日期期间的合理性
846
- valid,start1,end1=check_period(start,end)
847
- if not valid:
848
- print(" #Error(jensen_alpha_portfolio): incorrect start/end date(s)")
849
- return None,None
850
-
851
- #从字典中提取信息
852
- scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
853
- #检查份额配比是否合理
854
- """
855
- if round(sum(portionlist),1) != 1.0:
856
- print(" #Error(jensen_alpha_portfolio): incorrect total of portions.")
857
- return None,None
858
- """
859
-
860
- #获得期间的无风险收益率
861
- if isinstance(RF,bool):
862
- print(" Searching for risk-free interest rate ...")
863
- if scope=='China':
864
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
865
- else:
866
- rf_df=get_rf(start,end,scope=scope,freq='daily')
867
- if rf_df is None:
868
- message=" #Error("+func_name+"): "+"no data available for rf in"
869
- print(message,scope,start,end)
870
- return None,None
871
- RF=rf_df['RF'].mean()
872
-
873
- #抓取日投资组合价格:内含Mkt-RF和RF
874
- sp=get_portfolio_prices(portfolio,start,end,RF=RF)
875
- #计算日收益率,表示为百分比
876
- ret_pf=sp
877
- """
878
- import pandas as pd
879
- ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
880
- ret_pf=ret_pf.dropna()
881
-
882
- #强制转换索引格式,彻底消除下面并表的潜在隐患
883
- rf_df['ffdate']=rf_df.index.astype('str')
884
- rf_df['ffdate']=pd.to_datetime(rf_df['ffdate'])
885
- rf_df.set_index(['ffdate'],inplace=True)
886
-
887
- if rf_df is None:
888
- print(" #Error(jensen_alpha_portfolio): data source did not respond.")
889
- return None,None
890
- if len(rf_df) == 0:
891
- print(" #Error(jensen_alpha_portfolio): data source returned empty data.")
892
- return None,None
893
-
894
- #合并rf_df与ret_pf
895
- reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
896
- reg['Ret-RF']=reg['Close']-reg['RF']
897
- """
898
- reg=ret_pf
899
- reg=reg.dropna()
900
- if len(reg) == 0:
901
- print(" #Error(jensen_alpha_portfolio): empty data for regression.")
902
- return None,None
903
- ret_rf_mean=reg['Ret-RF'].mean()
904
-
905
- #使用CAPM回归计算投资组合的贝塔系数,这里得到的alpha就是Jensen's alpha
906
- from scipy import stats
907
- output=stats.linregress(reg['Mkt-RF'],reg['Ret-RF'])
908
- (beta,alpha,r_value,p_value,std_err)=output
909
-
910
- #打印报告
911
- if printout == True:
912
- date_start=str(reg.index[0].year)+'-'+str(reg.index[0].month)+ \
913
- '-'+str(reg.index[0].day)
914
- date_end=str(reg.index[-1].year)+'-'+str(reg.index[-1].month)+ \
915
- '-'+str(reg.index[-1].day)
916
- print("\n===== 风险调整收益率 =====")
917
- """
918
- _,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
919
- if len(tickerlist)==1:
920
- product=str(ticker_name(tickerlist,'bond'))
921
- else:
922
- product=str(ticker_name(tickerlist,'bond'))+' by '+str(sharelist)
923
- """
924
- print("证券资产:",portfolio_name(portfolio))
925
- print("样本期间:",date_start,"至",date_end,"(可用日期)")
926
- print("贝塔系数:",round(beta,4))
927
- print("风险溢价均值%:",round(ret_rf_mean,4))
928
- print("詹森阿尔法%:",round(alpha,4))
929
-
930
- import datetime as dt; today=dt.date.today()
931
- print("*数据来源:新浪/stooq/FRED,"+str(today))
932
-
933
- return alpha,ret_rf_mean,beta
934
-
935
-
936
- if __name__=='__main__':
937
- portfolio={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
938
- alpha1=jensen_alpha_portfolio(portfolio,'2019-01-01','2019-08-03')
939
-
940
- #==============================================================================
941
- def calc_monthly_date_range(start,end):
942
- """
943
- 功能:返回两个日期之间各个月份的开始和结束日期
944
- 输入:开始/结束日期
945
- 输出:两个日期之间各个月份的开始和结束日期元组对列表
946
- """
947
- #测试用
948
- #start='2019-01-05'
949
- #end='2019-06-25'
950
-
951
- import pandas as pd
952
- startdate=pd.to_datetime(start)
953
- enddate=pd.to_datetime(end)
954
-
955
- mdlist=[]
956
- #当月的结束日期
957
- syear=startdate.year
958
- smonth=startdate.month
959
- import calendar
960
- sdays=calendar.monthrange(syear,smonth)[1]
961
- from datetime import date
962
- slastday=pd.to_datetime(date(syear,smonth,sdays))
963
-
964
- if slastday > enddate: slastday=enddate
965
-
966
- #加入第一月的开始和结束日期
967
- import bisect
968
- bisect.insort(mdlist,(startdate,slastday))
969
-
970
- #加入结束月的开始和结束日期
971
- eyear=enddate.year
972
- emonth=enddate.month
973
- efirstday=pd.to_datetime(date(eyear,emonth,1))
974
- if startdate < efirstday:
975
- bisect.insort(mdlist,(efirstday,enddate))
976
-
977
- #加入期间内各个月份的开始和结束日期
978
- from dateutil.relativedelta import relativedelta
979
- next=startdate+relativedelta(months=+1)
980
- while next < efirstday:
981
- nyear=next.year
982
- nmonth=next.month
983
- nextstart=pd.to_datetime(date(nyear,nmonth,1))
984
- ndays=calendar.monthrange(nyear,nmonth)[1]
985
- nextend=pd.to_datetime(date(nyear,nmonth,ndays))
986
- bisect.insort(mdlist,(nextstart,nextend))
987
- next=next+relativedelta(months=+1)
988
-
989
- return mdlist
990
-
991
- if __name__=='__main__':
992
- mdp1=calc_monthly_date_range('2019-01-01','2019-06-30')
993
- mdp2=calc_monthly_date_range('2000-01-01','2000-06-30') #闰年
994
- mdp3=calc_monthly_date_range('2018-09-01','2019-03-31') #跨年
995
-
996
- for i in range(0,len(mdp1)):
997
- start=mdp1[i][0]
998
- end=mdp1[i][1]
999
- print("start =",start,"end =",end)
1000
-
1001
-
1002
- #==============================================================================
1003
- if __name__=='__main__':
1004
- portfolio={'Market':('US','^GSPC'),'JD':0.3,'BABA':0.7}
1005
- start='2019-01-01'
1006
- end='2019-03-31'
1007
- rar_type='sortino_ratio'
1008
-
1009
- def plot_rar_monthly(portfolio,start,end,rar_type):
1010
- """
1011
- 功能:将风险调整收益率和风险溢价逐月绘图对比
1012
- 输入:投资组合,开始/结束日期,风险调整收益指数类别
1013
- 输出:风险调整收益率和风险溢价的逐月数据框
1014
- 显示:按月绘图投资组合的风险调整收益率和风险溢价
1015
- """
1016
-
1017
- #检查日期期间的合理性
1018
- valid,start1,end1=check_period(start,end)
1019
- if not valid:
1020
- print(" #Error(plot_rar_monthly): incorrect start/end date(s)",start,end)
1021
- return None
1022
-
1023
- #检查投资组合各个成分股份额的合理性
1024
- """
1025
- if round(sum(portionlist),1) != 1.0:
1026
- print(" #Error(plot_rar_monthly): Incorrect total of portions")
1027
- return None
1028
- """
1029
-
1030
- #检查支持的rar_type
1031
- rar_list=['treynor_ratio','sharpe_ratio','sortino_ratio','jensen_alpha']
1032
- if rar_type not in rar_list:
1033
- print(" #Error(plot_rar_monthly): not supported rar type")
1034
- print(" Supported rar type:",rar_list)
1035
- return None
1036
-
1037
- #拆分start/end之间的各个年份和月份
1038
- mdlist=calc_monthly_date_range(start,end)
1039
- if len(mdlist) == 0:
1040
- print(" #Error(plot_rar_monthly): start/end dates inappropriate",start,end)
1041
- return None
1042
-
1043
- #用于保存risk premium和rar
1044
- print("\n Calculating monthly",rar_type,"......")
1045
- rarfunc=rar_type+'_portfolio'
1046
- rars=pd.DataFrame(columns=('YM','rp','rar'))
1047
- for i in range(0,len(mdlist)):
1048
- start=mdlist[i][0]
1049
- YM=start.strftime("%Y-%m")
1050
- print(' ',YM,end=' ')
1051
- end=mdlist[i][1]
1052
- rar,rp=eval(rarfunc)(portfolio,start,end,printout=False)
1053
-
1054
- row=pd.Series({'YM':YM,'rp':rp,'rar':rar})
1055
- try:
1056
- rars=rars.append(row,ignore_index=True)
1057
- except:
1058
- rars=rars._append(row,ignore_index=True)
1059
- print(" Searching completed.")
1060
- rars.set_index('YM',inplace=True)
1061
-
1062
- #绘图
1063
- plt.plot(rars['rp'],label='risk_premium',c='blue',marker='*',ls=':',lw=3)
1064
- plt.plot(rars['rar'],label=rar_type,c='r',lw=3,marker='o')
1065
- plt.axhline(y=0.0,color='black',linestyle=':',lw=1)
1066
- titletxt="投资组合的风险调整收益"
1067
- plt.title(titletxt)
1068
- plt.ylabel('收益率(%)')
1069
-
1070
- plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
1071
- plt.gca().set_facecolor('whitesmoke')
1072
-
1073
- #plt.xticks(rotation=30)
1074
- plt.legend(loc='best')
1075
-
1076
- import datetime as dt; today=dt.date.today()
1077
- footnote="数据来源:新浪/stooq/FRED,"+str(today)
1078
- plt.xlabel(footnote)
1079
-
1080
- plt.show()
1081
-
1082
- return
1083
-
1084
-
1085
- if __name__=='__main__':
1086
- portfolio={'Market':('US','^GSPC'),'VIPS':0.1,'PDD':0.2,'JD':0.3,'BABA':0.4}
1087
- plot_rar_monthly(portfolio,'2019-01-01','2019-06-30','treynor_ratio')
1088
-
1089
- portfolio={'Market':('US','^GSPC'),'AAPL':1.0}
1090
- plot_rar_monthly(portfolio,'2017-01-01','2017-12-31','sharpe_ratio')
1091
- #==============================================================================
1092
- #==============================================================================
1093
- if __name__=='__main__':
1094
- portfolio={'Market':('US','^GSPC'),'JD':0.3,'BABA':0.7}
1095
- start='2013-01-01'
1096
- end='2018-12-31'
1097
- rar_type='sortino_ratio'
1098
-
1099
-
1100
- def plot_rar_annual(portfolio,start,end,rar_type):
1101
- """
1102
- 功能:将风险调整收益率和风险溢价逐年绘图对比
1103
- 输入:投资组合,开始/结束日期,风险调整收益指数类别
1104
- 输出:风险调整收益率和风险溢价的逐年数据框
1105
- 显示:按年绘图投资组合的风险调整收益率和风险溢价
1106
- """
1107
- #检查日期期间的合理性
1108
- valid,start1,end1=check_period(start,end)
1109
- if not valid:
1110
- print(" #Error(plot_rar_annual): incorrect start/end date(s)",start.end)
1111
- return None
1112
-
1113
- #检查投资组合各个成分股份额的合理性
1114
- """
1115
- if round(sum(portionlist),1) != 1.0:
1116
- print(" #Error(plot_rar_annual): Incorrect total of portions")
1117
- return None
1118
- """
1119
-
1120
- #检查支持的rar_type
1121
- rar_list=['treynor_ratio','sharpe_ratio','sortino_ratio','jensen_alpha']
1122
- if rar_type not in rar_list:
1123
- print(" #Error(plot_rar_annual): not supported rar type")
1124
- print(" Supported rar type:",rar_list)
1125
- return None
1126
-
1127
- #拆分start/end之间的各个年份和月份
1128
- mdlist=calc_yearly_date_range(start,end)
1129
- if len(mdlist) == 0:
1130
- print(" #Error(plot_rar_annual): start/end dates inappropriate")
1131
- return None
1132
-
1133
- #用于保存risk premium和rar
1134
- print("\n Calculating yearly",rar_type,"......")
1135
- rarfunc=rar_type+'_portfolio'
1136
- rars=pd.DataFrame(columns=('YR','rp','rar'))
1137
- for i in range(0,len(mdlist)):
1138
- start=mdlist[i][0]
1139
- YR=start.strftime("%Y")
1140
- print(' ',YR,end=' ')
1141
- end=mdlist[i][1]
1142
- rar,rp=eval(rarfunc)(portfolio,start,end,printout=False)
1143
-
1144
- row=pd.Series({'YR':YR,'rp':rp,'rar':rar})
1145
- try:
1146
- rars=rars.append(row,ignore_index=True)
1147
- except:
1148
- rars=rars._append(row,ignore_index=True)
1149
- print(" Searching completed.")
1150
- rars.set_index('YR',inplace=True)
1151
-
1152
- #绘图
1153
- plt.plot(rars['rp'],label='risk_premium',c='blue',marker='*',ls=':',lw=3)
1154
- plt.plot(rars['rar'],label=rar_type,c='r',lw=3,marker='o')
1155
- plt.axhline(y=0.0,color='black',linestyle=':',lw=1)
1156
- titletxt="投资组合的风险调整收益"
1157
- plt.title(titletxt)
1158
- plt.ylabel('收益率(%)')
1159
-
1160
- plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
1161
- plt.gca().set_facecolor('whitesmoke')
1162
-
1163
- #plt.xticks(rotation=45)
1164
- plt.legend(loc='best')
1165
-
1166
- import datetime as dt; today=dt.date.today()
1167
- footnote="数据来源:新浪/stooq/FRED,"+str(today)
1168
- plt.xlabel(footnote)
1169
-
1170
- plt.show()
1171
-
1172
- return
1173
-
1174
-
1175
- if __name__=='__main__':
1176
- portfolio={'Market':('US','^GSPC'),'VIPS':0.1,'PDD':0.2,'JD':0.3,'BABA':0.4}
1177
- plot_rar_annual(portfolio,'2013-01-01','2019-06-30','treynor_ratio')
1178
-
1179
- portfolio={'Market':('US','^GSPC'),'AAPL':1.0}
1180
- plot_rar_annual(portfolio,'2015-01-01','2017-12-31','sharpe_ratio')
1181
- #==============================================================================
1182
- # 新加入的滚动指标对比
1183
- #==============================================================================
1184
- if __name__=='__main__':
1185
- portfolio1={'Market':('US','^GSPC'),'AAPL':1}
1186
- portfolio2={'Market':('US','^GSPC'),'MSFT':1}
1187
-
1188
- start='2020-1-1'
1189
- end ='2020-12-31'
1190
- scope='US'
1191
- ratio_name='sharpe'
1192
- window=30
1193
- graph=True
1194
-
1195
- def compare_rar_portfolio(portfolio1,portfolio2,start,end,ratio_name='sharpe', \
1196
- window=240,graph=True):
1197
- """
1198
- 功能:比较两个投资组合的风险调整收益率,并绘制曲线
1199
- 注意:无风险收益率有两个月的延迟
1200
- """
1201
-
1202
- #检查日期的合理性
1203
- result,startdate,enddate=check_period(start,end)
1204
- if result is None:
1205
- print(" #Error(compare_rar_portfolio): invalid period",start,end)
1206
- return None
1207
-
1208
- #检查支持的指标
1209
- ratio_list=['treynor','sharpe','sortino','alpha']
1210
- name_list=['特雷诺比率','夏普比率','索替诺比率','詹森阿尔法']
1211
- if ratio_name not in ratio_list:
1212
- message=" #Error(compare_rar_portfolio): "+"unsupported rar ratio type"
1213
- print(message,ratio_name)
1214
- return None
1215
-
1216
- #计算开始日期的提前量:假定每月有20个交易日
1217
- adjdays=int(window/20.0*30.0)+1
1218
- import siat.common as cmn
1219
- new_start=cmn.date_adjust(start, adjust=-adjdays)
1220
-
1221
- #获取第一个投资组合的数据
1222
- rars1=rar_ratio_rolling(portfolio1,new_start,end,ratio_name=ratio_name, \
1223
- window=window,graph=False)
1224
- if rars1 is None: return None
1225
- #获取第二个投资组合的数据
1226
- rars2=rar_ratio_rolling(portfolio2,new_start,end,ratio_name=ratio_name, \
1227
- window=window,graph=False)
1228
- if rars2 is None: return None
1229
-
1230
- #绘制双线图
1231
- ticker1="证券1"
1232
- colname1='RAR'
1233
- label1=name_list[ratio_list.index(ratio_name)]
1234
-
1235
- ticker2="证券2"
1236
- colname2='RAR'
1237
- label2=label1
1238
-
1239
- ylabeltxt=label1
1240
- titletxt="证券风险调整收益的滚动趋势对比"
1241
-
1242
- _,_,tickers1,shares1,ticker_type=decompose_portfolio(portfolio1)
1243
- if len(tickers1) == 1:
1244
- ticker1=tickers1[0]
1245
- pf1str=tickers1[0]
1246
- else:
1247
- pf1str=ticker1+':成分'+str(tickers1)+',比例'+str(shares1)
1248
-
1249
- _,_,tickers2,shares2,ticker_type=decompose_portfolio(portfolio2)
1250
- if len(tickers2) == 1:
1251
- ticker2=tickers2[0]
1252
- pf2str=tickers2[0]
1253
- else:
1254
- pf2str=ticker2+':成分'+str(tickers2)+',比例'+str(shares2)
1255
-
1256
- footnote="日期 -->"
1257
- if len(tickers1) > 1:
1258
- footnote=footnote+'\n'+pf1str
1259
- if len(tickers2) > 1:
1260
- footnote=footnote+'\n'+pf2str
1261
-
1262
- import datetime as dt; today=dt.date.today()
1263
- source="数据来源:新浪/stooq/FRED,"+str(today)
1264
- footnote=footnote+"\n"+source
1265
-
1266
- plot_line2(rars1,ticker1,colname1,label1, \
1267
- rars2,ticker2,colname2,label2, \
1268
- ylabeltxt,titletxt,footnote)
1269
-
1270
- #合并RAR
1271
- import pandas as pd
1272
- rarm=pd.merge(rars1,rars2,how='inner',left_index=True,right_index=True)
1273
- rars=rarm[['RAR_x','RAR_y']]
1274
- rars.rename(columns={'RAR_x':ticker1+'_'+ratio_name,'RAR_y':ticker2+'_'+ratio_name},inplace=True)
1275
-
1276
- return rars
1277
-
1278
-
1279
- if __name__=='__main__':
1280
- pf1={'Market':('US','^GSPC'),'AAPL':1}
1281
- pf2={'Market':('US','^GSPC'),'MSFT':1}
1282
- rars12=compare_rar_portfolio(pf1,pf2,'2019-11-1','2020-11-30')
1283
-
1284
- pfA={'Market':('China','000001.SS'),'600519.SS':1}
1285
- pfB={'Market':('China','000001.SS'),'000858.SZ':1}
1286
- rarsAB=compare_rar_portfolio(pfA,pfB,'2019-11-1','2020-11-30')
1287
-
1288
- pfbb={'Market':('US','^GSPC'),'BABA':1}
1289
- pfjd={'Market':('US','^GSPC'),'JD':1}
1290
- rarsbj=compare_rar_portfolio(pfbb,pfjd,'2019-11-1','2020-11-30')
1291
-
1292
- pfbb={'Market':('US','^GSPC'),'BABA':1}
1293
- pfpd={'Market':('US','^GSPC'),'PDD':1}
1294
- rarsbj=compare_rar_portfolio(pfbb,pfpd,'2019-11-1','2020-11-30')
1295
-
1296
- #==============================================================================
1297
- #==============================================================================
1298
-
1299
- if __name__=='__main__':
1300
- tickers = ['000858.SZ','600779.SS','000596.SZ','603589.SS']
1301
- start='2022-1-1'
1302
- end='2022-10-31'
1303
- rar_name="sharpe"
1304
- market_index="000300.SS"
1305
- market="China"
1306
-
1307
- tickers=['AAPL','01810.HK','000063.SZ']
1308
- start='2023-1-1'
1309
- end='2023-7-1'
1310
-
1311
- RF=False
1312
- window=240
1313
- axhline_value=0
1314
- axhline_label=''
1315
- graph=True
1316
- printout=True
1317
- sortby='tpw_mean'
1318
-
1319
- graph=False
1320
- source='auto'
1321
- trailing=20
1322
-
1323
- def compare_mrar(tickers,rar_name,start,end, \
1324
- market="China",market_index="000300.SS",RF=False,window=63, \
1325
- axhline_value=0,axhline_label='零线',graph=True,printout=True, \
1326
- sortby='tpw_mean',source='auto',trailing=20,trend_threshhold=0.001, \
1327
- annotate=False):
1328
- """
1329
- 功能:计算多只股票的rar比率,并绘图对比。多只股票必须处于同一个经济体的证券市场
1330
- 比率:支持夏普比率、特雷诺比率、索替诺比率、阿尔法比率
1331
-
1332
- sortby:
1333
- tpw_mean(近期优先加权平均值降序排列)
1334
- min(最小值降序排列)
1335
- mean(平均值降序排列)
1336
- median(中位数值降序排列)
1337
- trailing(短期趋势,最新数值与近trailing个交易日均值的差值降序排列)
1338
-
1339
- 注意:当RF=False时可能有bug
1340
- """
1341
- #检查tickers是否为列表且不少于两只股票
1342
- tickers=upper_ticker(tickers)
1343
-
1344
- #检查rar指标的种类
1345
- rarlist=['treynor','sharpe','sortino','alpha']
1346
- if not (rar_name.lower() in rarlist):
1347
- print(" #Error(compare_mrar): unsupported rar name",rar_name)
1348
- return None
1349
-
1350
- # 去掉重复代码
1351
- tickers=list(set(tickers))
1352
-
1353
- #检查支持的比率种类
1354
-
1355
- #检查日期的合理性
1356
-
1357
- #将开始日期提前
1358
- start1=date_adjust(start,-(int(window/20*31)+1))
1359
-
1360
- import os,sys
1361
- class HiddenPrints:
1362
- def __enter__(self):
1363
- self._original_stdout = sys.stdout
1364
- sys.stdout = open(os.devnull, 'w')
1365
-
1366
- def __exit__(self, exc_type, exc_val, exc_tb):
1367
- sys.stdout.close()
1368
- sys.stdout = self._original_stdout
1369
-
1370
- import pandas as pd
1371
- df=pd.DataFrame()
1372
- print(" Starting to retrieve and calculate",rar_name,"ratio, please wait ......")
1373
- for t in tickers:
1374
-
1375
- pf={'Market':(market,market_index),t:1.0}
1376
- #关闭print输出
1377
- with HiddenPrints():
1378
- df_tmp=rar_ratio_rolling(pf,start1,end,ratio_name=rar_name, \
1379
- RF=RF,window=window,graph=False,source=source)
1380
-
1381
- if df_tmp is None:
1382
- print(" #Warning(compare_mrar): data not available for",t)
1383
- continue
1384
- else:
1385
- dft=df_tmp[['RAR']]
1386
- #dft.rename(columns={'RAR':ticker_name(t)},inplace=True)
1387
- dft.rename(columns={'RAR':t},inplace=True)
1388
-
1389
- if len(df)==0:
1390
- #第一个
1391
- df=dft
1392
- else:
1393
- df=pd.merge(df,dft,how='outer',left_index=True,right_index=True)
1394
-
1395
- if len(df)==0:
1396
- print(" #Warning(compare_mrar): no data available for the above securities between",start,end)
1397
- return None
1398
-
1399
- # 填充空缺值
1400
- df.fillna(method='ffill',inplace=True) #使用前值填充
1401
- df.fillna(method='bfill',inplace=True) #使用后值填充
1402
-
1403
- #绘制多条曲线
1404
- rar_list=['treynor','sortino','sharpe','alpha']
1405
- rar_list_e=['Treynor Ratio','Sortino Ratio','Sharpe Ratio','Jensen alpha']
1406
- #rar_list_c=['特雷诺比率','索替诺比率','夏普比率','阿尔法指数']
1407
- rar_list_c=['特雷诺比率','索替诺比率','夏普比率','阿尔法指标']
1408
-
1409
- pos=rar_list.index(rar_name)
1410
-
1411
- import datetime; today = datetime.date.today()
1412
-
1413
- lang=check_language()
1414
- if lang == 'English':
1415
-
1416
- y_label=rar_list_e[pos]
1417
- x_label="Source: sina/stooq, "+str(today)
1418
- title_txt="Compare Multiple Risk-adjusted Return Performance"
1419
- else:
1420
- y_label=rar_list_c[pos]
1421
- x_label="数据来源: 新浪/stooq/Yahoo,"+str(today)
1422
- title_txt="比较多只证券的风险调整收益滚动指标"
1423
-
1424
- startpd=pd.to_datetime(start)
1425
- df1=df[df.index >= startpd]
1426
-
1427
- # 是否绘图
1428
- if graph:
1429
- # 翻译证券名称
1430
- for c in list(df1):
1431
- df1.rename(columns={c:ticker_name(c)},inplace=True)
1432
-
1433
- draw_lines(df1,y_label,x_label, \
1434
- axhline_value=axhline_value,axhline_label=axhline_label, \
1435
- title_txt=title_txt,data_label=False,annotate=annotate)
1436
-
1437
- if printout:
1438
-
1439
- dfcols=list(df)
1440
- for c in dfcols:
1441
- ccn=ticker_name(c)+'('+c+')'
1442
- df.rename(columns={c:ccn},inplace=True)
1443
-
1444
- if sortby=='tpw_mean':
1445
- sortby_txt='按推荐标记+近期优先加权平均值降序排列'
1446
- elif sortby=='min':
1447
- sortby_txt='按推荐标记+最小值降序排列'
1448
- elif sortby=='mean':
1449
- sortby_txt='按推荐标记+平均值降序排列'
1450
- elif sortby=='median':
1451
- sortby_txt='按推荐标记+中位数值降序排列'
1452
- elif sortby=='trailing':
1453
- sortby_txt='按推荐标记+短期均值走势降序排列'
1454
- else:
1455
- pass
1456
-
1457
- title_txt='*** '+title_txt+':'+y_label+','+sortby_txt
1458
- additional_note="*** 注:列表仅显示有星号标记或特定数量的证券。"
1459
- footnote='期间趋势范围:'+str(start)+'至'+str(end)+";近期趋势范围:近"+str(trailing)+"个交易日"
1460
- descriptive_statistics(df,title_txt,additional_note+footnote,decimals=4, \
1461
- sortby=sortby,recommend_only=True,trailing=trailing, \
1462
- trend_threshhold=trend_threshhold)
1463
-
1464
- return df1
1465
-
1466
- if __name__=='__main__':
1467
- tickers = ['000858.SZ','600779.SS','000596.SZ','603589.SS','000001.SS']
1468
- df=compare_mrar(tickers,'sharpe','2022-1-1','2022-10-31')
1469
- df=compare_mrar(tickers,'alpha','2022-10-1','2022-10-31')
1470
-
1471
- #==============================================================================
1472
-
1473
- if __name__=='__main__':
1474
- ticker = '000858.SZ'
1475
- start='2022-1-1'
1476
- end='2022-10-31'
1477
- rar_names=["sharpe",'sortino','alpha']
1478
- market_index="000300.SS"
1479
- market="China"
1480
-
1481
- RF=False
1482
- window=60
1483
- axhline_value=0
1484
- axhline_label=''
1485
- graph=True
1486
- printout=False
1487
- sortby='tpw_mean'
1488
-
1489
- graph=False
1490
- source='auto'
1491
- trailing=20
1492
-
1493
- def compare_1security_mrar(ticker,rar_names,start,end, \
1494
- market="China",market_index="000300.SS",RF=False,window=63, \
1495
- axhline_value=0,axhline_label='零线',graph=True,printout=False, \
1496
- sortby='tpw_mean',source='auto',trailing=20,trend_threshhold=0.001, \
1497
- annotate=False):
1498
- """
1499
- 功能:计算一只股票的多个rar比率,并绘图对比
1500
- 比率:支持夏普比率、特雷诺比率、索替诺比率、阿尔法比率等
1501
-
1502
- sortby:
1503
- tpw_mean(近期优先加权平均值降序排列)
1504
- min(最小值降序排列)
1505
- mean(平均值降序排列)
1506
- median(中位数值降序排列)
1507
- trailing(短期趋势,最新数值与近trailing个交易日均值的差值降序排列)
1508
- """
1509
- DEBUG=True
1510
-
1511
- rar_list=['treynor','sharpe','sortino','alpha']
1512
- rar_list_c=['特雷诺比率','夏普比率','索替诺比率','阿尔法指标']
1513
-
1514
- valid,start1,end1=check_period(start,end)
1515
- if not valid:
1516
- print(" #Error(compare_1security_mrar): invalid period from",start,'to',end)
1517
- return None
1518
-
1519
- if isinstance(ticker,str):
1520
- tickers=[ticker]
1521
- elif isinstance(ticker,list):
1522
- tickers=[ticker[0]]
1523
- else:
1524
- print(" #Warning(compare_1security_mrar): unsupported ticker for",ticker)
1525
- return None
1526
-
1527
- import pandas as pd
1528
- df=pd.DataFrame()
1529
- for r in rar_names:
1530
- if not (r in rar_list):
1531
- print(" #Warning(compare_1security_mrar): unsupported rar indicator for",r)
1532
- continue
1533
-
1534
- dft=compare_mrar(tickers=tickers,rar_name=r, \
1535
- start=start,end=end, \
1536
- market=market,market_index=market_index, \
1537
- RF=RF,window=window, \
1538
- axhline_value=0,axhline_label='零线', \
1539
- graph=False,printout=False, \
1540
- sortby=sortby,source=source,trailing=trailing,trend_threshhold=trend_threshhold)
1541
- if dft is None:
1542
- print(" #Error(compare_1security_mrar): information unaccessible for",tickers[0])
1543
- break
1544
- if len(dft)==0:
1545
- print(" #Error(compare_1security_mrar): information unavailable for",tickers[0],'from',start,'to',end)
1546
- break
1547
-
1548
- pos=rar_list.index(r)
1549
- rcn=rar_list_c[pos]
1550
- dft.columns=[rcn]
1551
-
1552
- if len(df)==0:
1553
- df=dft
1554
- else:
1555
- df=pd.merge(df,dft,how='inner',left_index=True,right_index=True)
1556
-
1557
- if len(df)==0:
1558
- return None
1559
-
1560
- import datetime; today = datetime.date.today()
1561
- y_label="风险调整收益指标"
1562
- x_label="数据来源: 综合新浪/stooq/Yahoo,"+str(today)
1563
- title_txt="证券风险调整收益滚动指标:"+ticker_name(tickers[0])
1564
-
1565
- # 是否绘图
1566
- if graph:
1567
- draw_lines(df,y_label,x_label, \
1568
- axhline_value=axhline_value,axhline_label=axhline_label, \
1569
- title_txt=title_txt,data_label=False,annotate=annotate)
1570
-
1571
- return df
1572
-
1573
-
1574
- #==============================================================================
1575
-
1576
-