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,2363 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- 本模块功能:证券投资组合理论优化分析
4
- 所属工具包:证券投资分析工具SIAT
5
- SIAT:Security Investment Analysis Tool
6
- 创建日期:2020年7月1日
7
- 最新修订日期:2020年7月29日
8
- 作者:王德宏 (WANG Dehong, Peter)
9
- 作者单位:北京外国语大学国际商学院
10
- 作者邮件:wdehong2000@163.com
11
- 版权所有:王德宏
12
- 用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
13
- 特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
14
- """
15
- #==============================================================================
16
- #统一屏蔽一般性警告
17
- import warnings; warnings.filterwarnings("ignore")
18
- #==============================================================================
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
-
26
- import pandas as pd
27
- import numpy as np
28
- import datetime
29
- #==============================================================================
30
- import seaborn as sns
31
- import matplotlib.pyplot as plt
32
- #统一设定绘制的图片大小:数值为英寸,1英寸=100像素
33
- #plt.rcParams['figure.figsize']=(12.8,7.2)
34
- plt.rcParams['figure.figsize']=(12.8,6.4)
35
- plt.rcParams['figure.dpi']=300
36
- plt.rcParams['font.size'] = 13
37
- plt.rcParams['xtick.labelsize']=11 #横轴字体大小
38
- plt.rcParams['ytick.labelsize']=11 #纵轴字体大小
39
-
40
- title_txt_size=16
41
- ylabel_txt_size=14
42
- xlabel_txt_size=14
43
- legend_txt_size=14
44
-
45
- #设置绘图风格:网格虚线
46
- plt.rcParams['axes.grid']=True
47
- #plt.rcParams['grid.color']='steelblue'
48
- #plt.rcParams['grid.linestyle']='dashed'
49
- #plt.rcParams['grid.linewidth']=0.5
50
- #plt.rcParams['axes.facecolor']='whitesmoke'
51
-
52
- #处理绘图汉字乱码问题
53
- import sys; czxt=sys.platform
54
- if czxt in ['win32','win64']:
55
- plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
56
- mpfrc={'font.family': 'SimHei'}
57
- sns.set_style('whitegrid',{'font.sans-serif':['simhei','Arial']})
58
-
59
- if czxt in ['darwin','linux']: #MacOSX
60
- #plt.rcParams['font.family'] = ['Arial Unicode MS'] #用来正常显示中文标签
61
- plt.rcParams['font.family']= ['Heiti TC']
62
- mpfrc={'font.family': 'Heiti TC'}
63
- sns.set_style('whitegrid',{'font.sans-serif':['Arial Unicode MS','Arial']})
64
-
65
-
66
- # 解决保存图像时'-'显示为方块的问题
67
- plt.rcParams['axes.unicode_minus'] = False
68
- #==============================================================================
69
- #全局变量定义
70
- RANDOM_SEED=1234567890
71
-
72
- #==============================================================================
73
- def portfolio_config(tickerlist,sharelist):
74
- """
75
- 将股票列表tickerlist和份额列表sharelist合成为一个字典
76
- """
77
- #整理sharelist的小数点
78
- ratiolist=[]
79
- for s in sharelist:
80
- ss=round(s,4); ratiolist=ratiolist+[ss]
81
- #合成字典
82
- new_dict=dict(zip(tickerlist,ratiolist))
83
- return new_dict
84
-
85
- #==============================================================================
86
- def ratiolist_round(sharelist,num=4):
87
- """
88
- 将股票份额列表sharelist中的数值四舍五入
89
- """
90
- #整理sharelist的小数点
91
- ratiolist=[]
92
- for s in sharelist:
93
- ss=round(s,num); ratiolist=ratiolist+[ss]
94
- return ratiolist
95
-
96
- #==============================================================================
97
- def varname(p):
98
- """
99
- 功能:获得变量的名字本身。
100
- """
101
- import inspect
102
- import re
103
- for line in inspect.getframeinfo(inspect.currentframe().f_back)[3]:
104
- m = re.search(r'\bvarname\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)', line)
105
- if m:
106
- return m.group(1)
107
-
108
- #==============================================================================
109
- if __name__=='__main__':
110
- end_date='2021-12-3'
111
- pastyears=3
112
-
113
- def get_start_date(end_date,pastyears=1):
114
- """
115
- 输入参数:一个日期,年数
116
- 输出参数:几年前的日期
117
- start_date, end_date是datetime类型
118
- """
119
- import pandas as pd
120
- try:
121
- end_date=pd.to_datetime(end_date)
122
- except:
123
- print(" #Error(get_start_date): invalid date,",end_date)
124
- return None
125
-
126
- from datetime import datetime,timedelta
127
- start_date=datetime(end_date.year-pastyears,end_date.month,end_date.day)
128
- start_date=start_date-timedelta(days=1)
129
- # 日期-1是为了保证计算收益率时得到足够的样本数量
130
-
131
- start=start_date.strftime("%Y-%m-%d")
132
-
133
- return start
134
-
135
- #==============================================================================
136
- #==============================================================================
137
- #==============================================================================
138
- if __name__=='__main__':
139
- retgroup=StockReturns
140
-
141
- def cumulative_returns_plot(retgroup,name_list="",titletxt="投资组合策略:业绩比较", \
142
- ylabeltxt="持有收益率",xlabeltxt="", \
143
- label_list=[]):
144
- """
145
- 功能:基于传入的name_list绘制多条持有收益率曲线,并从label_list中取出曲线标记
146
- 注意:最多绘制四条曲线,否则在黑白印刷时无法区分曲线,以此标记为实线、点虚线、划虚线和点划虚线四种
147
- """
148
- if name_list=="":
149
- name_list=list(retgroup)
150
-
151
- if len(label_list) < len(name_list):
152
- label_list=name_list
153
-
154
- if xlabeltxt=="":
155
- #取出观察期
156
- hstart0=retgroup.index[0]; hstart=str(hstart0.date())
157
- hend0=retgroup.index[-1]; hend=str(hend0.date())
158
-
159
- lang = check_language()
160
- import datetime as dt; stoday=dt.date.today()
161
- if lang == 'Chinese':
162
- footnote1="观察期间: "+hstart+'至'+hend
163
- footnote2="\n数据来源:Sina/EM/Stooq/Yahoo, "+str(stoday)
164
- else:
165
- footnote1="Period of observation: "+hstart+' to '+hend
166
- footnote2="\nData source: Sina/EM/Stooq/Yahoo, "+str(stoday)
167
-
168
- xlabeltxt=footnote1+footnote2
169
-
170
- # 持有收益曲线绘制函数
171
- lslist=['-','--',':','-.']
172
- markerlist=['.','h','+','x','4','3','2','1']
173
- for name in name_list:
174
- pos=name_list.index(name)
175
- rlabel=label_list[pos]
176
- if pos < len(lslist):
177
- thisls=lslist[pos]
178
- else:
179
- thisls=(45,(55,20))
180
-
181
- # 计算持有收益率
182
- CumulativeReturns = ((1+retgroup[name]).cumprod()-1)
183
- if pos-len(lslist) < 0:
184
- CumulativeReturns.plot(label=ectranslate(rlabel),ls=thisls)
185
- else:
186
- thismarker=markerlist[pos-len(lslist)]
187
- CumulativeReturns.plot(label=ectranslate(rlabel),ls=thisls,marker=thismarker,markersize=4)
188
-
189
- plt.axhline(y=0,ls=":",c="red")
190
- plt.legend(loc='best')
191
- plt.title(titletxt); plt.ylabel(ylabeltxt); plt.xlabel(xlabeltxt)
192
-
193
- plt.gca().set_facecolor('whitesmoke')
194
- plt.show()
195
-
196
- return
197
-
198
- if __name__=='__main__':
199
- retgroup=StockReturns
200
- cumulative_returns_plot(retgroup,name_list,titletxt,ylabeltxt,xlabeltxt, \
201
- label_list=[])
202
-
203
- def portfolio_expret_plot(retgroup,name_list="",titletxt="投资组合策略:业绩比较", \
204
- ylabeltxt="持有收益率",xlabeltxt="", \
205
- label_list=[]):
206
- """
207
- 功能:套壳函数cumulative_returns_plot
208
- """
209
-
210
- cumulative_returns_plot(retgroup,name_list,titletxt,ylabeltxt,xlabeltxt,label_list)
211
-
212
- return
213
-
214
- #==============================================================================
215
- def portfolio_hpr(portfolio,thedate,pastyears=1, \
216
- rate_period='1Y',rate_type='shibor',RF=True, \
217
- printout=True,graph=True):
218
- """
219
- 功能:套壳函数portfolio_cumret
220
- """
221
- dflist=portfolio_cumret(portfolio=portfolio,thedate=thedate,pastyears=pastyears, \
222
- rate_period=rate_period,rate_type=rate_type,RF=RF, \
223
- printout=printout,graph=graph)
224
-
225
- return dflist
226
-
227
- #==============================================================================
228
- if __name__=='__main__':
229
- Market={'Market':('US','^GSPC')}
230
- Market={'Market':('US','^GSPC','我的组合001')}
231
- Stocks1={'AAPL':.3,'MSFT':.15,'AMZN':.15,'FB':.01,'GOOG':.01}
232
- Stocks2={'XOM':.02,'JNJ':.02,'JPM':.01,'TSLA':.3,'SBUX':.03}
233
- portfolio=dict(Market,**Stocks1,**Stocks2)
234
-
235
- ticker_name(portfolio)
236
-
237
- thedate='2023-2-17'
238
- pastyears=1
239
- rate_period='1Y'
240
- rate_type='shibor'
241
- RF=False
242
- printout=True
243
-
244
- def portfolio_cumret(portfolio,thedate,pastyears=1, \
245
- rate_period='1Y',rate_type='shibor',RF=False, \
246
- printout=True,graph=True):
247
- """
248
- 功能:绘制投资组合的累计收益率趋势图,并与等权和期间内交易额加权组合比较
249
- 注意:中国部分历史区段的treasury历史可能无法取得;
250
- 无论是shibor还是treasury的近期利率均可能空缺,只能以最近期的数值填补
251
- """
252
- print("\n Searching for portfolio info, which may take time ...")
253
- # 解构投资组合
254
- scope,_,tickerlist,sharelist0,ticker_type=decompose_portfolio(portfolio)
255
- pname=portfolio_name(portfolio)
256
-
257
- #如果持仓份额总数不为1,则将其转换为总份额为1
258
- import numpy as np
259
- totalshares=np.sum(sharelist0)
260
- if abs(totalshares - 1) >= 0.00001:
261
- print("\n #Warning(portfolio_cumret): total weights is",totalshares,"\b, expecting 1.0 here")
262
- print(" Action: automatically converted into total weights 1.0")
263
- sharelist=list(sharelist0/totalshares)
264
- else:
265
- sharelist=sharelist0
266
-
267
- #..........................................................................
268
- # 计算历史数据的开始日期
269
- start=get_start_date(thedate,pastyears)
270
-
271
- #一次性获得无风险利率,传递给后续函数,避免后续每次获取,耗费时间
272
- if RF:
273
- rf_df=get_rf_daily(start,thedate,scope,rate_period,rate_type)
274
- #结果字段中,RF是日利率百分比,rf_daily是日利率数值
275
- if rf_df is None:
276
- #print(" #Error(portfolio_cumret): failed to retrieve risk-free interest rate in",scope)
277
- print(" #Warning: all subsequent portfolio optimizations cannot proceed")
278
- print(" Solution1: try again after until success to include risk-free interest rate in calculation")
279
- print(" Solution2: use RF=False in script command to ignore risk-free interest rate in calculation")
280
- return None
281
- else:
282
- rf_df=None
283
-
284
- #..........................................................................
285
- import os, sys
286
- class HiddenPrints:
287
- def __enter__(self):
288
- self._original_stdout = sys.stdout
289
- sys.stdout = open(os.devnull, 'w')
290
-
291
- def __exit__(self, exc_type, exc_val, exc_tb):
292
- sys.stdout.close()
293
- sys.stdout = self._original_stdout
294
-
295
- # 抓取投资组合股价
296
- #prices=get_prices(tickerlist,start,thedate)
297
-
298
- if printout:
299
- prices=get_prices_simple(tickerlist,start,thedate)
300
- else:
301
- with HiddenPrints():
302
- prices=get_prices_simple(tickerlist,start,thedate)
303
- ntickers=len(list(prices['Close']))
304
- nrecords=len(prices)
305
- #print(" Successfully retrieved",ntickers,"stocks with",nrecords,"record(s) respectively")
306
- print(" Successfully retrieved prices of",ntickers,"stocks for",pname)
307
-
308
- if prices is None:
309
- print(" #Error(portfolio_cumret): failed to get portfolio prices",pname)
310
- return None
311
- if len(prices) == 0:
312
- print(" #Error(portfolio_cumret): retrieved empty prices for",pname)
313
- return None
314
- #..........................................................................
315
-
316
- # 取各个成份股的收盘价
317
- aclose=prices['Close']
318
- member_prices=aclose
319
- # 计算各个成份股的日收益率,并丢弃缺失值
320
- StockReturns = aclose.pct_change().dropna()
321
- if len(StockReturns) == 0:
322
- print("\n #Error(portfolio_cumret): retrieved empty returns for",pname)
323
- return None
324
-
325
- # 保存各个成份股的收益率数据,为了后续调用的方便
326
- stock_return = StockReturns.copy()
327
-
328
- # 将原投资组合的权重存储为numpy数组类型,为了合成投资组合计算方便
329
- import numpy as np
330
- portfolio_weights = np.array(sharelist)
331
- # 合成portfolio的日收益率
332
- WeightedReturns = stock_return.mul(portfolio_weights, axis=1)
333
- # 原投资组合的收益率
334
- StockReturns['Portfolio'] = WeightedReturns.sum(axis=1)
335
- #..........................................................................
336
- lang = check_language()
337
- #..........................................................................
338
-
339
- # 绘制原投资组合的收益率曲线,以便使用收益率%来显示
340
- if graph:
341
- plotsr = StockReturns['Portfolio']
342
- plotsr.plot(label=pname)
343
- plt.axhline(y=0,ls=":",c="red")
344
-
345
- if lang == 'Chinese':
346
- title_txt="投资组合: 日收益率的变化趋势"
347
- ylabel_txt="日收益率"
348
- source_txt="数据来源: Sina/EM/Stooq/Yahoo, "
349
- else:
350
- title_txt="Investment Portfolio: Daily Return"
351
- ylabel_txt="Daily Return"
352
- source_txt="Data source: Sina/EM/Stooq/Yahoo, "
353
-
354
- plt.title(title_txt)
355
- plt.ylabel(ylabel_txt)
356
-
357
- stoday = datetime.date.today()
358
- plt.xlabel(source_txt+str(stoday))
359
- plt.legend()
360
-
361
- plt.gca().set_facecolor('whitesmoke')
362
- plt.show()
363
- #..........................................................................
364
-
365
- # 计算原投资组合的持有收益率,并绘图
366
- name_list=["Portfolio"]
367
- label_list=[pname]
368
-
369
- if lang == 'Chinese':
370
- titletxt="投资组合: 持有收益率的变化趋势"
371
- ylabeltxt="持有收益率"
372
- xlabeltxt="数据来源: Sina/EM/Stooq/Yahoo, "+str(stoday)
373
- else:
374
- titletxt="Investment Portfolio: Holding Return"
375
- ylabeltxt="Holding Return"
376
- xlabeltxt="Data source: Sina/EM/Stooq/Yahoo, "+str(stoday)
377
-
378
- #绘制持有收益率曲线
379
- if graph:
380
- cumulative_returns_plot(StockReturns,name_list,titletxt,ylabeltxt,xlabeltxt,label_list)
381
- #..........................................................................
382
-
383
- # 构造等权重组合Portfolio_EW的持有收益率
384
- numstocks = len(tickerlist)
385
- # 平均分配每一项的权重
386
- portfolio_weights_ew = np.repeat(1/numstocks, numstocks)
387
- # 合成等权重组合的收益,按行横向加总
388
- StockReturns['Portfolio_EW']=stock_return.mul(portfolio_weights_ew,axis=1).sum(axis=1)
389
- #..........................................................................
390
-
391
- # 创建交易额加权组合:按照成交金额计算期间内交易额均值
392
- tamount=prices['Close']*prices['Volume']
393
- tamountlist=tamount.mean(axis=0) #求列的均值
394
- tamountlist_array = np.array(tamountlist)
395
- # 计算成交金额权重
396
- portfolio_weights_lw = tamountlist_array / np.sum(tamountlist_array)
397
- # 计算成交金额加权的组合收益
398
- StockReturns['Portfolio_LW'] = stock_return.mul(portfolio_weights_lw, axis=1).sum(axis=1)
399
-
400
- #绘制累计收益率对比曲线
401
- if lang == 'Chinese':
402
- title_txt="投资组合策略:业绩对比"
403
- Portfolio_EW_txt="等权重策略"
404
- Portfolio_LW_txt="交易额加权策略"
405
- else:
406
- title_txt="Investment Portfolio Strategies: Performance Comparison"
407
- Portfolio_EW_txt="Equal-weight"
408
- Portfolio_LW_txt="Amount-weight"
409
-
410
- name_list=['Portfolio', 'Portfolio_EW', 'Portfolio_LW']
411
- label_list=[pname, Portfolio_EW_txt, Portfolio_LW_txt]
412
- titletxt=title_txt
413
-
414
- #绘制各个投资组合的持有收益率曲线
415
- if graph:
416
- cumulative_returns_plot(StockReturns,name_list,titletxt,ylabeltxt,xlabeltxt,label_list)
417
-
418
- #打印各个投资组合的持股比例
419
- member_returns=stock_return
420
- if printout:
421
- portfolio_expectation_universal(pname,member_returns,portfolio_weights,member_prices)
422
- portfolio_expectation_universal(Portfolio_EW_txt,member_returns,portfolio_weights_ew,member_prices)
423
- portfolio_expectation_universal(Portfolio_LW_txt,member_returns,portfolio_weights_lw,member_prices)
424
-
425
- #返回投资组合的综合信息
426
- member_returns=stock_return
427
- portfolio_returns=StockReturns[name_list]
428
-
429
- #投资组合名称改名
430
- portfolio_returns=cvt_portfolio_name(pname,portfolio_returns)
431
-
432
- #打印现有投资组合策略的排名
433
- if printout:
434
- portfolio_ranks(portfolio_returns,pname)
435
-
436
- return [[portfolio,thedate,member_returns,rf_df,member_prices], \
437
- [portfolio_returns,portfolio_weights,portfolio_weights_ew,portfolio_weights_lw]]
438
-
439
- if __name__=='__main__':
440
- X=portfolio_cumret(portfolio,'2021-9-30')
441
-
442
- if __name__=='__main__':
443
- pf_info=portfolio_cumret(portfolio,'2021-9-30')
444
-
445
- #==============================================================================
446
-
447
- def portfolio_expret(portfolio,today,pastyears=1, \
448
- rate_period='1Y',rate_type='shibor',RF=False,printout=True,graph=True):
449
- """
450
- 功能:绘制投资组合的持有期收益率趋势图,并与等权和期间内交易额加权组合比较
451
- 套壳原来的portfolio_cumret函数,以维持兼容性
452
- expret: expanding return,以维持与前述章节名词的一致性
453
- hpr: holding period return, 持有(期)收益率
454
- 注意:实验发现RF对于结果的影响极其微小难以观察,默认设为不使用无风险利率调整收益,以加快运行速度
455
- """
456
- #处理失败的返回值
457
- results=portfolio_cumret(portfolio,today,pastyears, \
458
- rate_period,rate_type,RF,printout,graph)
459
- if results is None: return None
460
-
461
- [[portfolio,thedate,member_returns,rf_df,member_prices], \
462
- [portfolio_returns,portfolio_weights,portfolio_weights_ew,portfolio_weights_lw]] = results
463
-
464
- return [[portfolio,thedate,member_returns,rf_df,member_prices], \
465
- [portfolio_returns,portfolio_weights,portfolio_weights_ew,portfolio_weights_lw]]
466
-
467
- if __name__=='__main__':
468
- pf_info=portfolio_expret(portfolio,'2021-9-30')
469
-
470
- #==============================================================================
471
- def portfolio_corr(pf_info):
472
- """
473
- 功能:绘制投资组合成份股之间相关关系的热力图
474
- """
475
- [[portfolio,thedate,stock_return,_,_],_]=pf_info
476
- pname=portfolio_name(portfolio)
477
-
478
- #取出观察期
479
- hstart0=stock_return.index[0]; hstart=str(hstart0.date())
480
- hend0=stock_return.index[-1]; hend=str(hend0.date())
481
-
482
- sr=stock_return.copy()
483
- collist=list(sr)
484
- for col in collist:
485
- #投资组合中名称翻译以债券优先处理,因此几乎没有人把基金作为成分股
486
- sr.rename(columns={col:ticker_name(col,'bond')},inplace=True)
487
-
488
- # 计算相关矩阵
489
- correlation_matrix = sr.corr()
490
-
491
- # 导入seaborn
492
- import seaborn as sns
493
- # 创建热图
494
- sns.heatmap(correlation_matrix,annot=True,cmap="YlGnBu",linewidths=0.3,
495
- annot_kws={"size": 16})
496
- plt.title(pname+": 成份股收益率之间的相关系数")
497
- plt.ylabel("成份股票")
498
-
499
- footnote1="观察期间: "+hstart+'至'+hend
500
- import datetime as dt; stoday=dt.date.today()
501
- footnote2="\n数据来源:Sina/EM/Stooq/Yahoo, "+str(stoday)
502
- plt.xlabel(footnote1+footnote2)
503
- plt.xticks(rotation=90); plt.yticks(rotation=0)
504
-
505
- plt.gca().set_facecolor('whitesmoke')
506
- plt.show()
507
-
508
- return
509
-
510
- if __name__=='__main__':
511
- Market={'Market':('US','^GSPC','我的组合001')}
512
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
513
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
514
- portfolio=dict(Market,**Stocks1,**Stocks2)
515
- pf_info=portfolio_expret(portfolio,'2019-12-31')
516
-
517
- portfolio_corr(pf_info)
518
- #==============================================================================
519
- def portfolio_covar(pf_info):
520
- """
521
- 功能:计算投资组合成份股之间的协方差
522
- """
523
- [[portfolio,thedate,stock_return,_,_],_]=pf_info
524
- pname=portfolio_name(portfolio)
525
-
526
- #取出观察期
527
- hstart0=stock_return.index[0]; hstart=str(hstart0.date())
528
- hend0=stock_return.index[-1]; hend=str(hend0.date())
529
-
530
- # 计算协方差矩阵
531
- cov_mat = stock_return.cov()
532
- # 年化协方差矩阵,252个交易日
533
- cov_mat_annual = cov_mat * 252
534
-
535
- # 导入seaborn
536
- import seaborn as sns
537
- # 创建热图
538
- sns.heatmap(cov_mat_annual,annot=True,cmap="YlGnBu",linewidths=0.3,
539
- annot_kws={"size": 8})
540
- plt.title(pname+": 成份股之间的协方差")
541
- plt.ylabel("成份股票")
542
-
543
- footnote1="观察期间: "+hstart+'至'+hend
544
- import datetime as dt; stoday=dt.date.today()
545
- footnote2="\n数据来源:Sina/EM/Stooq/Yahoo, "+str(stoday)
546
- plt.xlabel(footnote1+footnote2)
547
- plt.xticks(rotation=90)
548
- plt.yticks(rotation=0)
549
-
550
- plt.gca().set_facecolor('whitesmoke')
551
- plt.show()
552
-
553
- return
554
-
555
- #==============================================================================
556
- def portfolio_expectation_original(pf_info):
557
- """
558
- 功能:计算原始投资组合的年均收益率和标准差
559
- 输入:pf_info
560
- 输出:年化收益率和标准差
561
- """
562
- [[portfolio,_,member_returns,_,member_prices],[_,portfolio_weights,_,_]]=pf_info
563
- pname=portfolio_name(portfolio)
564
-
565
- portfolio_expectation_universal(pname,member_returns,portfolio_weights,member_prices)
566
-
567
- return
568
-
569
- if __name__=='__main__':
570
- Market={'Market':('US','^GSPC','我的组合001')}
571
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
572
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
573
- portfolio=dict(Market,**Stocks1,**Stocks2)
574
- pf_info=portfolio_expret(portfolio,'2019-12-31')
575
-
576
- portfolio_expectation(pf_info)
577
-
578
- #==============================================================================
579
- def portfolio_expectation_universal(pname,member_returns,portfolio_weights,member_prices):
580
- """
581
- 功能:计算给定成份股收益率和持股权重的投资组合年均收益率和标准差
582
- 输入:投资组合名称,成份股历史收益率数据表,投资组合权重series
583
- 输出:年化收益率和标准差
584
- 用途:求出MSR、GMV等持仓策略后计算投资组合的年化收益率和标准差
585
- """
586
-
587
- #观察期
588
- hstart0=member_returns.index[0]; hstart=str(hstart0.date())
589
- hend0=member_returns.index[-1]; hend=str(hend0.date())
590
- tickerlist=list(member_returns)
591
-
592
- #合成投资组合的历史收益率,按行横向加权求和
593
- preturns=member_returns.copy() #避免改变输入的数据
594
- preturns['Portfolio']=preturns.mul(portfolio_weights,axis=1).sum(axis=1)
595
-
596
- #计算一手投资组合的价格,最小持股份额的股票需要100股
597
- import numpy as np
598
- min_weight=np.min(portfolio_weights)
599
- # 将最少持股的股票份额转换为1
600
- portfolio_weights_1=portfolio_weights / min_weight * 1
601
- portfolio_values=member_prices.mul(portfolio_weights_1,axis=1).sum(axis=1)
602
- portfolio_value_thedate=portfolio_values[-1:].values[0]
603
-
604
- #计算年化收益率:按列求均值,需要有选项:滚动的年化收益率或月度收益率?
605
- mean_return=preturns['Portfolio'].mean(axis=0)
606
- annual_return = (1 + mean_return)**252 - 1
607
-
608
- #计算年化标准差
609
- std_return=preturns['Portfolio'].std(axis=0)
610
- import numpy as np
611
- annual_std = std_return*np.sqrt(252)
612
-
613
- lang=check_language()
614
- import datetime as dt; stoday=dt.date.today()
615
- if lang == 'Chinese':
616
- print("\n ======= 投资组合的收益与风险 =======")
617
- print(" 投资组合:",pname)
618
- print(" 分析日期:",str(hend))
619
- # 投资组合中即使持股比例最低的股票每次交易最少也需要1手(100股)
620
- print(" 期末1手组合单位价值:","约"+str(round(portfolio_value_thedate/10000*100,2))+"万")
621
- print(" 观察期间:",hstart+'至'+hend)
622
- print(" 年化收益率:",round(annual_return,4))
623
- print(" 年化标准差:",round(annual_std,4))
624
- print(" ***投资组合持仓策略***")
625
- print_tickerlist_sharelist(tickerlist,portfolio_weights,4)
626
-
627
- print(" *数据来源:Sina/EM/Stooq/Yahoo, "+str(stoday))
628
- else:
629
- print("\n ======= Investment Portfolio: Return and Risk =======")
630
- print(" Investment portfolio:",pname)
631
- print(" Date of analysis:",str(hend))
632
- print(" Value of portfolio:","about "+str(round(portfolio_value_thedate/1000,2))+"K/portfolio unit")
633
- print(" Period of observation:",hstart+' to '+hend)
634
- print(" Annualized return:",round(annual_return,4))
635
- print(" Annualized std of return:",round(annual_std,4))
636
- print(" ***Portfolio Constructing Strategy***")
637
- print_tickerlist_sharelist(tickerlist,portfolio_weights,4)
638
-
639
- print(" *Data source: Sina/EM/Stooq/Yahoo, "+str(stoday))
640
-
641
- return
642
-
643
- if __name__=='__main__':
644
- Market={'Market':('US','^GSPC','我的组合001')}
645
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
646
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
647
- portfolio=dict(Market,**Stocks1,**Stocks2)
648
- pf_info=portfolio_expret(portfolio,'2019-12-31')
649
-
650
- [[portfolio,thedate,member_returns,_,_],[_,portfolio_weights,_,_]]=pf_info
651
- pname=portfolio_name(portfolio)
652
-
653
- portfolio_expectation2(pname,member_returns, portfolio_weights)
654
-
655
- #==============================================================================
656
- def portfolio_expectation(pname,pf_info,portfolio_weights):
657
- """
658
- 功能:计算给定pf_info和持仓权重的投资组合年均收益率和标准差
659
- 输入:投资组合名称,pf_info,投资组合权重series
660
- 输出:年化收益率和标准差
661
- 用途:求出持仓策略后计算投资组合的年化收益率和标准差,为外部独立使用方便
662
- """
663
- [[_,_,member_returns,_,member_prices],_]=pf_info
664
-
665
- portfolio_expectation_universal(pname,member_returns,portfolio_weights,member_prices)
666
-
667
- return
668
-
669
- if __name__=='__main__':
670
- Market={'Market':('US','^GSPC','我的组合001')}
671
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
672
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
673
- portfolio=dict(Market,**Stocks1,**Stocks2)
674
- pf_info=portfolio_expret(portfolio,'2019-12-31')
675
-
676
- [[portfolio,thedate,member_returns,_,_],[_,portfolio_weights,_,_]]=pf_info
677
- pname=portfolio_name(portfolio)
678
-
679
- portfolio_expectation2(pname,member_returns, portfolio_weights)
680
-
681
-
682
- #==============================================================================
683
- def portfolio_ranks(portfolio_returns,pname):
684
- """
685
- 功能:区分中英文
686
- """
687
- lang = check_language()
688
- if lang == 'Chinese':
689
- df=portfolio_ranks_cn(portfolio_returns=portfolio_returns,pname=pname)
690
- else:
691
- df=portfolio_ranks_en(portfolio_returns=portfolio_returns,pname=pname)
692
-
693
- return df
694
-
695
- #==============================================================================
696
-
697
- def portfolio_ranks_cn(portfolio_returns,pname):
698
- """
699
- 功能:打印现有投资组合的收益率、标准差排名,收益率降序,标准差升序,中文
700
- """
701
- #临时保存,避免影响原值
702
- pr=portfolio_returns.copy()
703
-
704
- #以pname组合作为基准
705
- import numpy as np
706
- mean_return_pname=pr[pname].mean(axis=0)
707
- annual_return_pname=round(((1 + mean_return_pname)**252 - 1)*100,2)
708
- """
709
- if annual_return_pname > 0:
710
- pct_style=True #百分比模式
711
- else: #数值模式,直接加减
712
- pct_style=False
713
- """
714
- pct_style=False
715
-
716
- std_return_pname=pr[pname].std(axis=0)
717
- annual_std_pname= round((std_return_pname*np.sqrt(252))*100,2)
718
-
719
- import pandas as pd
720
- #prr=pd.DataFrame(columns=["名称","年化收益率","收益率变化","年化标准差","标准差变化","收益/风险"])
721
- prr=pd.DataFrame(columns=["名称","年化收益率%","收益率变化","年化标准差%","标准差变化","收益/风险"])
722
- cols=list(pr)
723
- for c in cols:
724
-
725
- #年化收益率:按列求均值
726
- mean_return=pr[c].mean(axis=0)
727
- annual_return = round(((1 + mean_return)**252 - 1)*100,2)
728
-
729
- if pct_style:
730
- return_chg=round((annual_return - annual_return_pname) / annual_return_pname * 100,2)
731
- else:
732
- return_chg=round((annual_return - annual_return_pname),2)
733
-
734
- #收益率变化
735
- if return_chg==0:
736
- return_chg_str="基准"
737
- elif return_chg > 0:
738
- if pct_style:
739
- return_chg_str='+'+str(return_chg)+'%'
740
- else:
741
- return_chg_str='+'+str(return_chg)
742
- else:
743
- if pct_style:
744
- return_chg_str='-'+str(-return_chg)+'%'
745
- else:
746
- return_chg_str='-'+str(-return_chg)
747
-
748
- #年化标准差
749
- std_return=pr[c].std(axis=0)
750
- annual_std = round((std_return*np.sqrt(252))*100,2)
751
-
752
- sharpe_ratio=round(annual_return / annual_std,2)
753
-
754
- if pct_style:
755
- std_chg=round((annual_std - annual_std_pname) / annual_std_pname * 100,1)
756
- else:
757
- std_chg=round((annual_std - annual_std_pname),2)
758
-
759
- #标准差变化
760
- if std_chg==0:
761
- std_chg_str="基准"
762
- elif std_chg > 0:
763
- if pct_style:
764
- std_chg_str='+'+str(std_chg)+'%'
765
- else:
766
- std_chg_str='+'+str(std_chg)
767
- else:
768
- if pct_style:
769
- std_chg_str='-'+str(-std_chg)+'%'
770
- else:
771
- std_chg_str='-'+str(-std_chg)
772
-
773
- row=pd.Series({"名称":c,"年化收益率%":annual_return, \
774
- "收益率变化":return_chg_str, \
775
- "年化标准差%":annual_std,"标准差变化":std_chg_str,"收益/风险":sharpe_ratio})
776
- try:
777
- prr=prr.append(row,ignore_index=True)
778
- except:
779
- prr=prr._append(row,ignore_index=True)
780
-
781
- #先按风险降序排名,高者排前面
782
- prr.sort_values(by="年化标准差%",ascending=False,inplace=True)
783
- prr.reset_index(inplace=True)
784
- prr['风险排名']=prr.index+1
785
-
786
- #再按收益降序排名,高者排前面
787
- prr.sort_values(by="年化收益率%",ascending=False,inplace=True)
788
- prr.reset_index(inplace=True)
789
- prr['收益排名']=prr.index+1
790
-
791
- #prr2=prr[["名称","收益排名","风险排名","年化收益率","年化标准差","收益率变化","标准差变化","收益/风险"]]
792
- prr2=prr[["名称","收益排名","年化收益率%","收益率变化", \
793
- "风险排名","年化标准差%","标准差变化", \
794
- "收益/风险"]]
795
- prr2.sort_values(by="年化收益率%",ascending=False,inplace=True)
796
- #prr2.reset_index(inplace=True)
797
-
798
- #打印
799
- print("\n========= 投资组合策略排名:平衡收益与风险 =========\n")
800
- #打印对齐
801
- pd.set_option('display.max_columns', 1000)
802
- pd.set_option('display.width', 1000)
803
- pd.set_option('display.max_colwidth', 1000)
804
- pd.set_option('display.unicode.ambiguous_as_wide', True)
805
- pd.set_option('display.unicode.east_asian_width', True)
806
-
807
- #print(prr2.to_string(index=False,header=False))
808
- #print(prr2.to_string(index=False))
809
-
810
- alignlist=['left']+['center']*(len(list(prr2))-2)+['right']
811
- print(prr2.to_markdown(index=False,tablefmt='plain',colalign=alignlist))
812
-
813
- return prr2
814
-
815
- if __name__=='__main__':
816
- portfolio_ranks(portfolio_returns,pname)
817
-
818
- #==============================================================================
819
-
820
- def portfolio_ranks_en(portfolio_returns,pname):
821
- """
822
- 功能:打印现有投资组合的收益率、标准差排名,收益率降序,标准差升序,英文
823
- """
824
- #临时保存,避免影响原值
825
- pr=portfolio_returns.copy()
826
-
827
- #以pname组合作为基准
828
- import numpy as np
829
- mean_return_pname=pr[pname].mean(axis=0)
830
- annual_return_pname=(1 + mean_return_pname)**252 - 1
831
- if annual_return_pname > 0:
832
- pct_style=True
833
- else:
834
- pct_style=False
835
-
836
- std_return_pname=pr[pname].std(axis=0)
837
- annual_std_pname= std_return_pname*np.sqrt(252)
838
-
839
- import pandas as pd
840
- prr=pd.DataFrame(columns=["Portfolio","Annualized Return","Change of Return","Annualized Std","Change of Std","Return/Risk"])
841
- cols=list(pr)
842
- for c in cols:
843
- #计算年化收益率:按列求均值
844
- mean_return=pr[c].mean(axis=0)
845
- annual_return = (1 + mean_return)**252 - 1
846
-
847
- if pct_style:
848
- return_chg=round((annual_return - annual_return_pname) / annual_return_pname *100,1)
849
- else:
850
- return_chg=round((annual_return - annual_return_pname),5)
851
-
852
- if return_chg==0:
853
- return_chg_str="base"
854
- elif return_chg > 0:
855
- if pct_style:
856
- return_chg_str='+'+str(return_chg)+'%'
857
- else:
858
- return_chg_str='+'+str(return_chg)
859
- else:
860
- if pct_style:
861
- return_chg_str='-'+str(-return_chg)+'%'
862
- else:
863
- return_chg_str='-'+str(-return_chg)
864
-
865
- #计算年化标准差
866
- std_return=pr[c].std(axis=0)
867
- annual_std = std_return*np.sqrt(252)
868
-
869
- sharpe_ratio=round(annual_return / annual_std,2)
870
-
871
- if pct_style:
872
- std_chg=round((annual_std - annual_std_pname) / annual_std_pname *100,1)
873
- else:
874
- std_chg=round((annual_std - annual_std_pname),5)
875
- if std_chg==0:
876
- std_chg_str="base"
877
- elif std_chg > 0:
878
- if pct_style:
879
- std_chg_str='+'+str(std_chg)+'%'
880
- else:
881
- std_chg_str='+'+str(std_chg)
882
- else:
883
- if pct_style:
884
- std_chg_str='-'+str(-std_chg)+'%'
885
- else:
886
- std_chg_str='-'+str(-std_chg)
887
-
888
- row=pd.Series({"Portfolio":c,"Annualized Return":annual_return,"Change of Return":return_chg_str, \
889
- "Annualized Std":annual_std,"Change of Std":std_chg_str,"Return/Risk":sharpe_ratio})
890
- try:
891
- prr=prr.append(row,ignore_index=True)
892
- except:
893
- prr=prr._append(row,ignore_index=True)
894
-
895
- #处理小数位数,以便与其他地方的小数位数一致
896
- prr['Annualized Return']=round(prr['Annualized Return'],4)
897
- prr['Annualized Std']=round(prr['Annualized Std'],4)
898
-
899
- #先按风险降序排名,高者排前面
900
- prr.sort_values(by="Annualized Std",ascending=False,inplace=True)
901
- prr.reset_index(inplace=True)
902
- prr['Risk Rank']=prr.index+1
903
-
904
- #再按收益降序排名,高者排前面
905
- prr.sort_values(by="Annualized Return",ascending=False,inplace=True)
906
- prr.reset_index(inplace=True)
907
- prr['Return Rank']=prr.index+1
908
-
909
- prr2=prr[["Portfolio","Return Rank","Risk Rank","Annualized Return","Annualized Std","Change of Return","Change of Std","Return/Risk"]]
910
- prr2.sort_values(by="Annualized Return",ascending=False,inplace=True)
911
- #prr2.reset_index(inplace=True)
912
-
913
- #打印
914
- print("\n========= Investment Portfolio Strategy Ranking: Balancing Return & Risk =========\n")
915
- #打印对齐
916
- pd.set_option('display.max_columns', 1000)
917
- pd.set_option('display.width', 1000)
918
- pd.set_option('display.max_colwidth', 1000)
919
- pd.set_option('display.unicode.ambiguous_as_wide', True)
920
- pd.set_option('display.unicode.east_asian_width', True)
921
-
922
- #print(prr2.to_string(index=False,header=False))
923
- print(prr2.to_string(index=False))
924
-
925
- return prr2
926
-
927
- #==============================================================================
928
- if __name__=='__main__':
929
- simulation=1000
930
- simulation=50000
931
-
932
- def portfolio_es(pf_info,simulation=50000):
933
- """
934
- 功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的年均收益率和标准差,绘制投资组合的可行集
935
- """
936
- [[portfolio,thedate,stock_return,_,_],_]=pf_info
937
- pname=portfolio_name(portfolio)
938
- _,_,tickerlist,_,ticker_type=decompose_portfolio(portfolio)
939
-
940
- #取出观察期
941
- hstart0=stock_return.index[0]; hstart=str(hstart0.date())
942
- hend0=stock_return.index[-1]; hend=str(hend0.date())
943
-
944
- #获得成份股个数
945
- numstocks=len(tickerlist)
946
-
947
- # 设置空的numpy数组,用于存储每次模拟得到的成份股权重、投资组合的收益率和标准差
948
- import numpy as np
949
- random_p = np.empty((simulation,numstocks+2))
950
- # 设置随机数种子,这里是为了结果可重复
951
- np.random.seed(RANDOM_SEED)
952
-
953
- # 循环模拟n次随机的投资组合
954
- print("\n Calculating portfolio efficient set, please wait ...")
955
- for i in range(simulation):
956
- # 生成numstocks个随机数,并归一化,得到一组随机的权重数据
957
- random9 = np.random.random(numstocks)
958
- random_weight = random9 / np.sum(random9)
959
-
960
- # 计算随机投资组合的年化平均收益率
961
- mean_return=stock_return.mul(random_weight,axis=1).sum(axis=1).mean(axis=0)
962
- annual_return = (1 + mean_return)**252 - 1
963
-
964
- # 计算随机投资组合的年化平均标准差
965
- std_return=stock_return.mul(random_weight,axis=1).sum(axis=1).std(axis=0)
966
- annual_std = std_return*np.sqrt(252)
967
-
968
- # 将上面生成的权重,和计算得到的收益率、标准差存入数组random_p中
969
- # 数组矩阵的前numstocks为随机权重,其后为年均收益率,再后为年均标准差
970
- random_p[i][:numstocks] = random_weight
971
- random_p[i][numstocks] = annual_return
972
- random_p[i][numstocks+1] = annual_std
973
-
974
- #显示完成进度
975
- print_progress_percent(i,simulation,steps=10,leading_blanks=2)
976
-
977
- # 将numpy数组转化成DataFrame数据框
978
- import pandas as pd
979
- RandomPortfolios = pd.DataFrame(random_p)
980
- # 设置数据框RandomPortfolios每一列的名称
981
- RandomPortfolios.columns = [ticker + "_weight" for ticker in tickerlist] \
982
- + ['Returns', 'Volatility']
983
-
984
- # 绘制散点图
985
- """
986
- RandomPortfolios.plot('Volatility','Returns',kind='scatter',color='y',edgecolors='k')
987
- """
988
- #RandomPortfolios['Returns_Volatility']=RandomPortfolios['Returns'] / RandomPortfolios['Volatility']
989
- #pf_ratio = np.array(RandomPortfolios['Returns_Volatility'])
990
- pf_ratio = np.array(RandomPortfolios['Returns'] / RandomPortfolios['Volatility'])
991
- pf_returns = np.array(RandomPortfolios['Returns'])
992
- pf_volatilities = np.array(RandomPortfolios['Volatility'])
993
-
994
- #plt.style.use('seaborn-dark') #不支持中文
995
- #plt.figure(figsize=(12.8,6.4))
996
- plt.scatter(pf_volatilities, pf_returns, c=pf_ratio,cmap='RdYlGn', edgecolors='black',marker='o')
997
- #plt.grid(True)
998
-
999
- import datetime as dt; stoday=dt.date.today()
1000
- lang = check_language()
1001
- if lang == 'Chinese':
1002
- if pname == '': pname='投资组合'
1003
-
1004
- plt.colorbar(label='收益率/标准差')
1005
- plt.title(pname+": 马科维茨可行(有效)集",fontsize=title_txt_size)
1006
- plt.ylabel("年化收益率",fontsize=ylabel_txt_size)
1007
-
1008
- footnote1="年化收益率标准差-->"
1009
- footnote2="\n\n基于给定的成份证券构造"+str(simulation)+"个投资组合"
1010
- footnote3="\n观察期间:"+hstart+"至"+hend
1011
- footnote4="\n数据来源: Sina/EM/Stooq/Yahoo, "+str(stoday)
1012
- else:
1013
- if pname == '': pname='Investment Portfolio'
1014
-
1015
- plt.colorbar(label='Return/Std')
1016
- plt.title(pname+": Efficient Set",fontsize=title_txt_size)
1017
- plt.ylabel("Annualized Return",fontsize=ylabel_txt_size)
1018
-
1019
- footnote1="Annualized Std -->\n\n"
1020
- footnote2="Based on given component securities, constructed "+str(simulation)+" portfolios\n"
1021
- footnote3="Period of sample: "+hstart+" to "+hend
1022
- footnote4="\nData Source: Sina/EM/Stooq/Yahoo, "+str(stoday)
1023
-
1024
- plt.xlabel(footnote1+footnote2+footnote3+footnote4,fontsize=xlabel_txt_size)
1025
-
1026
- plt.gca().set_facecolor('whitesmoke')
1027
- plt.show()
1028
-
1029
- return [pf_info,RandomPortfolios]
1030
-
1031
- if __name__=='__main__':
1032
- Market={'Market':('US','^GSPC','我的组合001')}
1033
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
1034
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
1035
- portfolio=dict(Market,**Stocks1,**Stocks2)
1036
- pf_info=portfolio_expret(portfolio,'2019-12-31')
1037
-
1038
- es=portfolio_es(pf_info,simulation=50000)
1039
-
1040
- #==============================================================================
1041
- if __name__=='__main__':
1042
- simulation=1000
1043
- rate_period='1Y'
1044
- rate_type='treasury'
1045
-
1046
- def portfolio_es_sharpe(pf_info,simulation=1000,rate_period='1Y',rate_type='treasury',RF=True):
1047
- """
1048
- 功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的年均风险溢价及其标准差,绘制投资组合的可行集
1049
- """
1050
- print(" Calculating possible portfolio combinations, please wait ...")
1051
-
1052
- [[portfolio,thedate,stock_return0,rf_df,_],_]=pf_info
1053
- pname=portfolio_name(portfolio)
1054
- scope,_,tickerlist,_,ticker_type=decompose_portfolio(portfolio)
1055
-
1056
- #取出观察期
1057
- hstart0=stock_return0.index[0]; hstart=str(hstart0.date())
1058
- hend0=stock_return0.index[-1]; hend=str(hend0.date())
1059
-
1060
- import pandas as pd
1061
- #获得期间内无风险利率
1062
- if RF:
1063
- #rf_df=get_rf_daily(hstart,hend,scope,rate_period,rate_type)
1064
- if not (rf_df is None):
1065
- stock_return1=pd.merge(stock_return0,rf_df,how='inner',left_index=True,right_index=True)
1066
- for t in tickerlist:
1067
- #计算风险溢价
1068
- stock_return1[t]=stock_return1[t]-stock_return1['rf_daily']
1069
-
1070
- stock_return=stock_return1[tickerlist]
1071
- else:
1072
- print(" #Error(portfolio_es_sharpe): failed to retrieve risk-free interest rate, please try again")
1073
- return None
1074
- else:
1075
- #不考虑RF
1076
- stock_return=stock_return0
1077
-
1078
- #获得成份股个数
1079
- numstocks=len(tickerlist)
1080
-
1081
- # 设置空的numpy数组,用于存储每次模拟得到的成份股权重、组合的收益率和标准差
1082
- import numpy as np
1083
- random_p = np.empty((simulation,numstocks+2))
1084
- # 设置随机数种子,这里是为了结果可重复
1085
- np.random.seed(RANDOM_SEED)
1086
-
1087
- # 循环模拟n次随机的投资组合
1088
- for i in range(simulation):
1089
- # 生成numstocks个随机数,并归一化,得到一组随机的权重数据
1090
- random9 = np.random.random(numstocks)
1091
- random_weight = random9 / np.sum(random9)
1092
-
1093
- # 计算随机投资组合的年化平均收益率
1094
- mean_return=stock_return.mul(random_weight,axis=1).sum(axis=1).mean(axis=0)
1095
- annual_return = (1 + mean_return)**252 - 1
1096
-
1097
- # 计算随机投资组合的年化平均标准差
1098
- std_return=stock_return.mul(random_weight,axis=1).sum(axis=1).std(axis=0)
1099
- annual_std = std_return*np.sqrt(252)
1100
-
1101
- # 将上面生成的权重,和计算得到的收益率、标准差存入数组random_p中
1102
- # 数组矩阵的前numstocks为随机权重,其后为年均收益率,再后为年均标准差
1103
- random_p[i][:numstocks] = random_weight
1104
- random_p[i][numstocks] = annual_return
1105
- random_p[i][numstocks+1] = annual_std
1106
-
1107
- #显示完成进度
1108
- print_progress_percent(i,simulation,steps=10,leading_blanks=2)
1109
-
1110
- # 将numpy数组转化成DataFrame数据框
1111
- RandomPortfolios = pd.DataFrame(random_p)
1112
- # 设置数据框RandomPortfolios每一列的名称
1113
- RandomPortfolios.columns = [ticker + "_weight" for ticker in tickerlist] \
1114
- + ['Risk premium', 'Risk premium volatility']
1115
-
1116
- return [pf_info,RandomPortfolios]
1117
-
1118
- if __name__=='__main__':
1119
- Market={'Market':('US','^GSPC','我的组合001')}
1120
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
1121
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
1122
- portfolio=dict(Market,**Stocks1,**Stocks2)
1123
- pf_info=portfolio_expret(portfolio,'2019-12-31')
1124
-
1125
- es_sharpe=portfolio_es_sharpe(pf_info,simulation=50000)
1126
-
1127
- #==============================================================================
1128
- if __name__=='__main__':
1129
- simulation=1000
1130
- rate_period='1Y'
1131
- rate_type='treasury'
1132
-
1133
- def portfolio_es_sortino(pf_info,simulation=1000,rate_period='1Y',rate_type='treasury',RF=True):
1134
- """
1135
- 功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的年均风险溢价及其下偏标准差,绘制投资组合的可行集
1136
- """
1137
- print(" Calculating possible portfolio combinations, please wait ...")
1138
-
1139
- [[portfolio,thedate,stock_return0,rf_df,_],_]=pf_info
1140
- pname=portfolio_name(portfolio)
1141
- scope,_,tickerlist,_,ticker_type=decompose_portfolio(portfolio)
1142
-
1143
- #取出观察期
1144
- hstart0=stock_return0.index[0]; hstart=str(hstart0.date())
1145
- hend0=stock_return0.index[-1]; hend=str(hend0.date())
1146
-
1147
- import pandas as pd
1148
- #获得期间内无风险利率
1149
- if RF:
1150
- #rf_df=get_rf_daily(hstart,hend,scope,rate_period,rate_type)
1151
- if not (rf_df is None):
1152
- stock_return1=pd.merge(stock_return0,rf_df,how='inner',left_index=True,right_index=True)
1153
- for t in tickerlist:
1154
- #计算风险溢价
1155
- stock_return1[t]=stock_return1[t]-stock_return1['rf_daily']
1156
-
1157
- stock_return=stock_return1[tickerlist]
1158
- else:
1159
- print(" #Error(portfolio_es_sortino): failed to retrieve risk-free interest rate, please try again")
1160
- return None
1161
- else:
1162
- #不考虑RF
1163
- stock_return=stock_return0
1164
-
1165
- #获得成份股个数
1166
- numstocks=len(tickerlist)
1167
-
1168
- # 设置空的numpy数组,用于存储每次模拟得到的成份股权重、组合的收益率和标准差
1169
- import numpy as np
1170
- random_p = np.empty((simulation,numstocks+2))
1171
- # 设置随机数种子,这里是为了结果可重复
1172
- np.random.seed(RANDOM_SEED)
1173
- # 与其他比率设置不同的随机数种子,意在产生多样性的随机组合
1174
-
1175
- # 循环模拟n次随机的投资组合
1176
- for i in range(simulation):
1177
- # 生成numstocks个随机数,并归一化,得到一组随机的权重数据
1178
- random9 = np.random.random(numstocks)
1179
- random_weight = random9 / np.sum(random9)
1180
-
1181
- # 计算随机投资组合的年化平均收益率
1182
- mean_return=stock_return.mul(random_weight,axis=1).sum(axis=1).mean(axis=0)
1183
- annual_return = (1 + mean_return)**252 - 1
1184
-
1185
- # 计算随机投资组合的年化平均下偏标准差
1186
- sr_temp0=stock_return.copy()
1187
- sr_temp0['Portfolio Ret']=sr_temp0.mul(random_weight,axis=1).sum(axis=1)
1188
- sr_temp1=sr_temp0[sr_temp0['Portfolio Ret'] < mean_return]
1189
- sr_temp2=sr_temp1[tickerlist]
1190
- lpsd_return=sr_temp2.mul(random_weight,axis=1).sum(axis=1).std(axis=0)
1191
- annual_lpsd = lpsd_return*np.sqrt(252)
1192
-
1193
- # 将上面生成的权重,和计算得到的收益率、标准差存入数组random_p中
1194
- # 数组矩阵的前numstocks为随机权重,其后为年均收益率,再后为年均标准差
1195
- random_p[i][:numstocks] = random_weight
1196
- random_p[i][numstocks] = annual_return
1197
- random_p[i][numstocks+1] = annual_lpsd
1198
-
1199
- #显示完成进度
1200
- print_progress_percent(i,simulation,steps=10,leading_blanks=2)
1201
-
1202
- # 将numpy数组转化成DataFrame数据框
1203
- RandomPortfolios = pd.DataFrame(random_p)
1204
- # 设置数据框RandomPortfolios每一列的名称
1205
- RandomPortfolios.columns = [ticker + "_weight" for ticker in tickerlist] \
1206
- + ['Risk premium', 'Risk premium LPSD']
1207
-
1208
- return [pf_info,RandomPortfolios]
1209
-
1210
- if __name__=='__main__':
1211
- Market={'Market':('US','^GSPC','我的组合001')}
1212
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
1213
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
1214
- portfolio=dict(Market,**Stocks1,**Stocks2)
1215
- pf_info=portfolio_expret(portfolio,'2019-12-31')
1216
-
1217
- es_sortino=portfolio_es_sortino(pf_info,simulation=50000)
1218
-
1219
- #==============================================================================
1220
- #==============================================================================
1221
- if __name__=='__main__':
1222
- simulation=1000
1223
- rate_period='1Y'
1224
- rate_type='treasury'
1225
-
1226
- def portfolio_es_alpha(pf_info,simulation=1000,rate_period='1Y',rate_type='treasury',RF=True):
1227
- """
1228
- 功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的年化标准差和阿尔法指数,绘制投资组合的可行集
1229
- """
1230
- print(" Calculating possible portfolio combinations, please wait ...")
1231
-
1232
- [[portfolio,thedate,stock_return0,rf_df,_],_]=pf_info
1233
- pname=portfolio_name(portfolio)
1234
- scope,mktidx,tickerlist,_,ticker_type=decompose_portfolio(portfolio)
1235
-
1236
- #取出观察期
1237
- hstart0=stock_return0.index[0]; hstart=str(hstart0.date())
1238
- hend0=stock_return0.index[-1]; hend=str(hend0.date())
1239
-
1240
- #计算市场指数的收益率
1241
- import pandas as pd
1242
- start1=date_adjust(hstart,adjust=-30)
1243
- mkt=get_prices(mktidx,start1,hend)
1244
- mkt['Mkt']=mkt['Close'].pct_change()
1245
- mkt.dropna(inplace=True)
1246
- mkt1=pd.DataFrame(mkt['Mkt'])
1247
-
1248
- stock_return0m=pd.merge(stock_return0,mkt1,how='left',left_index=True,right_index=True)
1249
- #获得期间内无风险利率
1250
- if RF:
1251
- #rf_df=get_rf_daily(hstart,hend,scope,rate_period,rate_type)
1252
- if not (rf_df is None):
1253
- stock_return1=pd.merge(stock_return0m,rf_df,how='inner',left_index=True,right_index=True)
1254
- for t in tickerlist:
1255
- #计算风险溢价
1256
- stock_return1[t]=stock_return1[t]-stock_return1['rf_daily']
1257
-
1258
- stock_return1['Mkt']=stock_return1['Mkt']-stock_return1['rf_daily']
1259
- stock_return=stock_return1[tickerlist+['Mkt']]
1260
- else:
1261
- print(" #Error(portfolio_es_alpha): failed to retrieve risk-free interest rate, please try again")
1262
- return None
1263
- else:
1264
- #不考虑RF
1265
- stock_return=stock_return0m[tickerlist+['Mkt']]
1266
-
1267
- #获得成份股个数
1268
- numstocks=len(tickerlist)
1269
-
1270
- # 设置空的numpy数组,用于存储每次模拟得到的成份股权重、组合的收益率和标准差
1271
- import numpy as np
1272
- random_p = np.empty((simulation,numstocks+2))
1273
- # 设置随机数种子,这里是为了结果可重复
1274
- np.random.seed(RANDOM_SEED)
1275
- # 与其他比率设置不同的随机数种子,意在产生多样性的随机组合
1276
-
1277
- # 循环模拟n次随机的投资组合
1278
- from scipy import stats
1279
- for i in range(simulation):
1280
- # 生成numstocks个随机数,并归一化,得到一组随机的权重数据
1281
- random9 = np.random.random(numstocks)
1282
- random_weight = random9 / np.sum(random9)
1283
-
1284
- # 计算随机投资组合的历史收益率
1285
- stock_return['pRet']=stock_return[tickerlist].mul(random_weight,axis=1).sum(axis=1)
1286
- """
1287
- #使用年化收益率,便于得到具有可比性的纵轴数据刻度
1288
- stock_return['pReta']=(1+stock_return['pRet'])**252 - 1
1289
- stock_return['Mkta']=(1+stock_return['Mkt'])**252 - 1
1290
- """
1291
- #回归求截距项作为阿尔法指数
1292
-
1293
- (beta,alpha,_,_,_)=stats.linregress(stock_return['Mkt'],stock_return['pRet'])
1294
- """
1295
- mean_return=stock_return[tickerlist].mul(random_weight,axis=1).sum(axis=1).mean(axis=0)
1296
- annual_return = (1 + mean_return)**252 - 1
1297
-
1298
- # 计算随机投资组合的年化平均标准差
1299
- std_return=stock_return[tickerlist].mul(random_weight,axis=1).sum(axis=1).std(axis=0)
1300
- annual_std = std_return*np.sqrt(252)
1301
- """
1302
- # 将上面生成的权重,和计算得到的阿尔法指数、贝塔系数存入数组random_p中
1303
- # 数组矩阵的前numstocks为随机权重,其后为收益指标,再后为风险指标
1304
- random_p[i][:numstocks] = random_weight
1305
- random_p[i][numstocks] = alpha
1306
- random_p[i][numstocks+1] = beta
1307
-
1308
- #显示完成进度
1309
- print_progress_percent(i,simulation,steps=10,leading_blanks=2)
1310
-
1311
- # 将numpy数组转化成DataFrame数据框
1312
- RandomPortfolios = pd.DataFrame(random_p)
1313
- # 设置数据框RandomPortfolios每一列的名称
1314
- RandomPortfolios.columns = [ticker + "_weight" for ticker in tickerlist] \
1315
- + ['alpha', 'beta']
1316
-
1317
- return [pf_info,RandomPortfolios]
1318
-
1319
- if __name__=='__main__':
1320
- Market={'Market':('US','^GSPC','我的组合001')}
1321
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
1322
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
1323
- portfolio=dict(Market,**Stocks1,**Stocks2)
1324
- pf_info=portfolio_expret(portfolio,'2019-12-31')
1325
-
1326
- es_alpha=portfolio_es_alpha(pf_info,simulation=50000)
1327
-
1328
- #==============================================================================
1329
- if __name__=='__main__':
1330
- simulation=1000
1331
- rate_period='1Y'
1332
- rate_type='treasury'
1333
-
1334
- def portfolio_es_treynor(pf_info,simulation=1000,rate_period='1Y',rate_type='treasury',RF=True):
1335
- """
1336
- 功能:基于随机数,生成大量可能的投资组合,计算各个投资组合的风险溢价和贝塔系数,绘制投资组合的可行集
1337
- """
1338
- print(" Calculating possible portfolio combinations, please wait ...")
1339
-
1340
- [[portfolio,_,stock_return0,rf_df,_],_]=pf_info
1341
- pname=portfolio_name(portfolio)
1342
- scope,mktidx,tickerlist,_,ticker_type=decompose_portfolio(portfolio)
1343
-
1344
- #取出观察期
1345
- hstart0=stock_return0.index[0]; hstart=str(hstart0.date())
1346
- hend0=stock_return0.index[-1]; hend=str(hend0.date())
1347
-
1348
- #计算市场指数的收益率
1349
- import pandas as pd
1350
- start1=date_adjust(hstart,adjust=-30)
1351
- mkt=get_prices(mktidx,start1,hend)
1352
- mkt['Mkt']=mkt['Close'].pct_change()
1353
- mkt.dropna(inplace=True)
1354
- mkt1=pd.DataFrame(mkt['Mkt'])
1355
-
1356
- stock_return0m=pd.merge(stock_return0,mkt1,how='left',left_index=True,right_index=True)
1357
- #获得期间内无风险利率
1358
- if RF:
1359
- #rf_df=get_rf_daily(hstart,hend,scope,rate_period,rate_type)
1360
- if not (rf_df is None):
1361
- stock_return1=pd.merge(stock_return0m,rf_df,how='inner',left_index=True,right_index=True)
1362
- for t in tickerlist:
1363
- #计算风险溢价
1364
- stock_return1[t]=stock_return1[t]-stock_return1['rf_daily']
1365
-
1366
- stock_return1['Mkt']=stock_return1['Mkt']-stock_return1['rf_daily']
1367
- stock_return=stock_return1[tickerlist+['Mkt']]
1368
- else:
1369
- print(" #Error(portfolio_es_treynor): failed to retrieve risk-free interest rate, please try again")
1370
- return None
1371
- else:
1372
- #不考虑RF
1373
- stock_return=stock_return0m[tickerlist+['Mkt']]
1374
-
1375
- #获得成份股个数
1376
- numstocks=len(tickerlist)
1377
-
1378
- # 设置空的numpy数组,用于存储每次模拟得到的成份股权重、组合的收益率和标准差
1379
- import numpy as np
1380
- random_p = np.empty((simulation,numstocks+2))
1381
- # 设置随机数种子,这里是为了结果可重复
1382
- np.random.seed(RANDOM_SEED)
1383
- # 与其他比率设置不同的随机数种子,意在产生多样性的随机组合
1384
-
1385
- # 循环模拟simulation次随机的投资组合
1386
- from scipy import stats
1387
- for i in range(simulation):
1388
- # 生成numstocks个随机数放入random9,计算成份股持仓比例放入random_weight,得到一组随机的权重数据
1389
- random9 = np.random.random(numstocks)
1390
- random_weight = random9 / np.sum(random9)
1391
-
1392
- # 计算随机投资组合的历史收益率
1393
- stock_return['pRet']=stock_return[tickerlist].mul(random_weight,axis=1).sum(axis=1)
1394
-
1395
- #回归求贝塔系数作为指数分母
1396
- (beta,alpha,_,_,_)=stats.linregress(stock_return['Mkt'],stock_return['pRet'])
1397
-
1398
- #计算年化风险溢价
1399
- mean_return=stock_return[tickerlist].mul(random_weight,axis=1).sum(axis=1).mean(axis=0)
1400
- annual_return = (1 + mean_return)**252 - 1
1401
- """
1402
- # 计算随机投资组合的年化平均标准差
1403
- std_return=stock_return.mul(random_weight,axis=1).sum(axis=1).std(axis=0)
1404
- annual_std = std_return*np.sqrt(252)
1405
- """
1406
- # 将上面生成的权重,和计算得到的风险溢价、贝塔系数存入数组random_p中
1407
- # 数组矩阵的前numstocks为随机权重,其后为收益指标,再后为风险指标
1408
- random_p[i][:numstocks] = random_weight
1409
- random_p[i][numstocks] = annual_return
1410
- random_p[i][numstocks+1] = beta
1411
-
1412
- #显示完成进度
1413
- print_progress_percent(i,simulation,steps=10,leading_blanks=2)
1414
-
1415
- # 将numpy数组转化成DataFrame数据框
1416
- RandomPortfolios = pd.DataFrame(random_p)
1417
-
1418
- # 设置数据框RandomPortfolios每一列的名称
1419
- RandomPortfolios.columns = [ticker + "_weight" for ticker in tickerlist] \
1420
- + ['Risk premium', 'beta']
1421
-
1422
- return [pf_info,RandomPortfolios]
1423
-
1424
- if __name__=='__main__':
1425
- Market={'Market':('US','^GSPC','我的组合001')}
1426
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
1427
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
1428
- portfolio=dict(Market,**Stocks1,**Stocks2)
1429
- pf_info=portfolio_expret(portfolio,'2019-12-31')
1430
-
1431
- es_treynor=portfolio_es_treynor(pf_info,simulation=50000)
1432
-
1433
- #==============================================================================
1434
- def RandomPortfolios_plot(RandomPortfolios,col_x,col_y,colorbartxt,title_ext, \
1435
- ylabeltxt,x_axis_name,pname,simulation,hstart,hend, \
1436
- hiret_point,lorisk_point):
1437
- """
1438
- 功能:将生成的马科维茨可行集RandomPortfolios绘制成彩色散点图
1439
- """
1440
-
1441
- """
1442
- #特雷诺比率,对照用
1443
- #RandomPortfolios.plot('beta','Risk premium',kind='scatter',color='y',edgecolors='k')
1444
- pf_ratio = np.array(RandomPortfolios['Risk premium'] / RandomPortfolios['beta'])
1445
- pf_returns = np.array(RandomPortfolios['Risk premium'])
1446
- pf_volatilities = np.array(RandomPortfolios['beta'])
1447
-
1448
- plt.figure(figsize=(12.8,6.4))
1449
- plt.scatter(pf_volatilities, pf_returns, c=pf_ratio,cmap='RdYlGn', edgecolors='black',marker='o')
1450
- plt.colorbar(label='特雷诺比率')
1451
-
1452
- plt.title("投资组合: 马科维茨可行集,基于特雷诺比率")
1453
- plt.ylabel("年化风险溢价")
1454
-
1455
- import datetime as dt; stoday=dt.date.today()
1456
- footnote1="贝塔系数-->"
1457
- footnote2="\n\n基于"+pname+"之成份股构造"+str(simulation)+"个投资组合"
1458
- footnote3="\n观察期间:"+hstart+"至"+hend
1459
- footnote4="\n来源: Sina/EM/stooq/fred, "+str(stoday)
1460
- plt.xlabel(footnote1+footnote2+footnote3+footnote4)
1461
- plt.show()
1462
- """
1463
-
1464
- #RandomPortfolios.plot(col_x,col_y,kind='scatter',color='y',edgecolors='k')
1465
-
1466
- pf_ratio = np.array(RandomPortfolios[col_y] / RandomPortfolios[col_x])
1467
- pf_returns = np.array(RandomPortfolios[col_y])
1468
- pf_volatilities = np.array(RandomPortfolios[col_x])
1469
-
1470
- #plt.figure(figsize=(12.8,6.4))
1471
- plt.scatter(pf_volatilities, pf_returns, c=pf_ratio,cmap='RdYlGn', edgecolors='black',marker='o')
1472
- plt.colorbar(label=colorbartxt)
1473
-
1474
- lang = check_language()
1475
- if lang == 'Chinese':
1476
- if pname == '': pname='投资组合'
1477
-
1478
- plt.title(pname+": 马科维茨有效(可行)集,基于"+title_ext,fontsize=title_txt_size)
1479
- plt.ylabel(ylabeltxt,fontsize=ylabel_txt_size)
1480
-
1481
- import datetime as dt; stoday=dt.date.today()
1482
- footnote1=x_axis_name+" -->\n\n"
1483
- footnote2="基于设定的成份证券构造"+str(simulation)+"个投资组合"
1484
- footnote3="\n观察期间:"+hstart+"至"+hend
1485
- footnote4="\n数据来源: Sina/EM/Stooq/Yahoo, "+str(stoday)
1486
- else:
1487
- if pname == '': pname='Investment Portfolio'
1488
-
1489
- plt.title(pname+": Efficient Set, Based on "+title_ext,fontsize=title_txt_size)
1490
- plt.ylabel(ylabeltxt,fontsize=ylabel_txt_size)
1491
-
1492
- import datetime as dt; stoday=dt.date.today()
1493
- footnote1=x_axis_name+" -->\n\n"
1494
- footnote2="Based on given component securities, constructed "+str(simulation)+" portfolios"
1495
- footnote3="\nPeriod of sample: "+hstart+" to "+hend
1496
- footnote4="\nData source: Sina/EM/Stooq/Yahoo, "+str(stoday)
1497
-
1498
- plt.xlabel(footnote1+footnote2+footnote3+footnote4,fontsize=xlabel_txt_size)
1499
-
1500
- #解析最大比率点和最低风险点信息,并绘点
1501
- [hiret_x,hiret_y,name_hiret]=hiret_point
1502
- #plt.scatter(hiret_x, hiret_y, color='red',marker='*',s=150,label=name_hiret)
1503
- plt.scatter(hiret_x, hiret_y, color='blue',marker='*',s=200,label=name_hiret)
1504
-
1505
- [lorisk_x,lorisk_y,name_lorisk]=lorisk_point
1506
- #plt.scatter(lorisk_x, lorisk_y, color='m',marker='8',s=100,label=name_lorisk)
1507
- plt.scatter(lorisk_x, lorisk_y, color='red',marker='8',s=150,label=name_lorisk)
1508
-
1509
- plt.legend(loc='best')
1510
-
1511
- plt.gca().set_facecolor('whitesmoke')
1512
- plt.show()
1513
-
1514
- return
1515
- #==============================================================================
1516
- #==============================================================================
1517
- def cvt_portfolio_name(pname,portfolio_returns):
1518
- """
1519
- 功能:将结果数据表中投资组合策略的名字从英文改为中文
1520
- 将原各处portfolio_optimize函数中的过程统一起来
1521
- """
1522
-
1523
- pelist=['Portfolio','Portfolio_EW','Portfolio_LW','Portfolio_MSR','Portfolio_GMVS', \
1524
- 'Portfolio_MSO','Portfolio_GML','Portfolio_MAR','Portfolio_GMBA', \
1525
- 'Portfolio_MTR','Portfolio_GMBT']
1526
-
1527
- lang=check_language()
1528
- if lang == "Chinese":
1529
- pclist=[pname,'等权重组合','交易额加权组合','MSR组合','GMVS组合','MSO组合','GML组合', \
1530
- 'MAR组合','GMBA组合', 'MTR组合','GMBT组合']
1531
- else:
1532
- pclist=[pname,'Equal-weight','Amount-weight','MSR','GMVS','MSO','GML', \
1533
- 'MAR','GMBA', 'MTR','GMBT']
1534
-
1535
- pecols=list(portfolio_returns)
1536
- for p in pecols:
1537
- try:
1538
- ppos=pelist.index(p)
1539
- except:
1540
- continue
1541
- else:
1542
- pc=pclist[ppos]
1543
- portfolio_returns.rename(columns={p:pc},inplace=True)
1544
-
1545
- return portfolio_returns
1546
-
1547
- #==============================================================================
1548
-
1549
- def portfolio_optimize_sharpe(es_info,RF=False,graph=True):
1550
- """
1551
- 功能:计算投资组合的最高夏普比率组合,并绘图
1552
- MSR: Maximium Sharpe Rate, 最高夏普指数方案
1553
- GMVS: Global Minimum Volatility by Sharpe, 全局最小波动方案
1554
- """
1555
-
1556
- #需要定制:定义名称变量......................................................
1557
- col_ratio='Sharpe' #指数名称
1558
- col_y='Risk premium' #指数分子
1559
- col_x='Risk premium volatility' #指数分母
1560
-
1561
- name_hiret='MSR' #Maximum Sharpe Ratio,指数最高点
1562
- name_lorisk='GMVS' #Global Minimum Volatility by Sharpe,风险最低点
1563
-
1564
- lang = check_language()
1565
- if lang == 'Chinese':
1566
- title_ext="夏普比率" #用于标题区别
1567
- if RF:
1568
- colorbartxt='夏普比率(经无风险利率调整后)' #用于彩色棒标签
1569
- ylabeltxt="年化风险溢价" #用于纵轴名称
1570
- x_axis_name="年化风险溢价标准差" #用于横轴名称
1571
- else:
1572
- colorbartxt='夏普比率(未经无风险利率调整)' #用于彩色棒标签
1573
- ylabeltxt="年化收益率" #用于纵轴名称
1574
- x_axis_name="年化标准差" #用于横轴名称
1575
- else:
1576
- title_ext="Sharpe Ratio" #用于标题区别
1577
- if RF:
1578
- colorbartxt='Sharpe Ratio(Rf adjusted)' #用于彩色棒标签
1579
- ylabeltxt="Annualized Risk Premium" #用于纵轴名称
1580
- x_axis_name="Annualized Std of Risk Premium" #用于横轴名称
1581
- else:
1582
- colorbartxt='Sharpe Ratio(Rf unadjusted)' #用于彩色棒标签
1583
- ylabeltxt="Annualized Return" #用于纵轴名称
1584
- x_axis_name="Annualized Std" #用于横轴名称
1585
-
1586
- #定制部分结束...............................................................
1587
-
1588
- #计算指数,寻找最大指数点和风险最低点,并绘图标注两个点
1589
- hiret_weights,lorisk_weights,portfolio_returns = \
1590
- portfolio_optimize_rar(es_info,col_ratio,col_y,col_x,name_hiret,name_lorisk, \
1591
- colorbartxt,title_ext,ylabeltxt,x_axis_name,graph=graph)
1592
-
1593
- return name_hiret,hiret_weights,name_lorisk,lorisk_weights,portfolio_returns
1594
-
1595
-
1596
- if __name__=='__main__':
1597
- Market={'Market':('US','^GSPC','我的组合001')}
1598
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
1599
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
1600
- portfolio=dict(Market,**Stocks1,**Stocks2)
1601
-
1602
- pf_info=portfolio_expret(portfolio,'2019-12-31')
1603
- es_sharpe=portfolio_es_sharpe(pf_info,simulation=50000)
1604
-
1605
- MSR_weights,GMV_weights,portfolio_returns=portfolio_optimize_sharpe(es_sharpe)
1606
-
1607
-
1608
- #==============================================================================
1609
-
1610
- def portfolio_optimize_sortino(es_info,RF=False,graph=True):
1611
- """
1612
- 功能:计算投资组合的最高索替诺比率组合,并绘图
1613
- MSO: Maximium Sortino ratio, 最高索替诺比率方案
1614
- GML: Global Minimum LPSD volatility, 全局最小LPSD下偏标准差方案
1615
- """
1616
-
1617
- #需要定制:定义名称变量......................................................
1618
- col_ratio='Sortino' #指数名称
1619
- col_y='Risk premium' #指数分子
1620
- col_x='Risk premium LPSD' #指数分母
1621
-
1622
- name_hiret='MSO' #Maximum SOrtino ratio,指数最高点
1623
- name_lorisk='GML' #Global Minimum LPSD,风险最低点
1624
-
1625
- title_ext="索替诺比率" #用于标题区别
1626
- if RF:
1627
- colorbartxt='索替诺比率(经无风险利率调整后)' #用于彩色棒标签
1628
- ylabeltxt="年化风险溢价" #用于纵轴名称
1629
- x_axis_name="年化风险溢价之下偏标准差" #用于横轴名称
1630
- else:
1631
- colorbartxt='索替诺比率(未经无风险利率调整)' #用于彩色棒标签
1632
- ylabeltxt="年化收益率" #用于纵轴名称
1633
- x_axis_name="年化下偏标准差" #用于横轴名称
1634
- #定制部分结束...............................................................
1635
-
1636
- #计算指数,寻找最大指数点和风险最低点,并绘图标注两个点
1637
- hiret_weights,lorisk_weights,portfolio_returns = \
1638
- portfolio_optimize_rar(es_info,col_ratio,col_y,col_x,name_hiret,name_lorisk, \
1639
- colorbartxt,title_ext,ylabeltxt,x_axis_name,graph=graph)
1640
-
1641
- return name_hiret,hiret_weights,name_lorisk,lorisk_weights,portfolio_returns
1642
-
1643
-
1644
- if __name__=='__main__':
1645
- Market={'Market':('US','^GSPC','我的组合001')}
1646
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
1647
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
1648
- portfolio=dict(Market,**Stocks1,**Stocks2)
1649
-
1650
- pf_info=portfolio_expret(portfolio,'2019-12-31')
1651
- es_sortino=portfolio_es_sortino(pf_info,simulation=50000)
1652
-
1653
- MSO_weights,GML_weights,portfolio_returns=portfolio_optimize_sortino(es_Sortino)
1654
-
1655
-
1656
- #==============================================================================
1657
-
1658
- def portfolio_optimize_alpha(es_info,RF=False,graph=True):
1659
- """
1660
- 功能:计算投资组合的最高詹森阿尔法组合,并绘图
1661
- MAR: Maximium Alpha Ratio, 最高阿尔法指数方案
1662
- GMBA: Global Minimum Beta by Alpha, 全局最小贝塔系数方案
1663
- """
1664
-
1665
- #需要定制:定义名称变量......................................................
1666
- col_ratio='Sharpe' #指数名称
1667
- col_y='alpha' #指数分子
1668
- col_x='beta' #指数分母
1669
-
1670
- name_hiret='MAR' #Maximum Alpha Ratio,指数最高点
1671
- name_lorisk='GMBA' #Global Minimum Beta by Alpha,风险最低点
1672
-
1673
- title_ext="阿尔法指数" #用于标题区别
1674
- if RF:
1675
- colorbartxt='阿尔法指数(经无风险利率调整后)' #用于彩色棒标签
1676
- else:
1677
- colorbartxt='阿尔法指数(未经无风险利率调整)' #用于彩色棒标签
1678
- ylabeltxt="阿尔法指数" #用于纵轴名称
1679
- x_axis_name="贝塔系数" #用于横轴名称
1680
- #定制部分结束...............................................................
1681
-
1682
- #计算指数,寻找最大指数点和风险最低点,并绘图标注两个点
1683
- hiret_weights,lorisk_weights,portfolio_returns = \
1684
- portfolio_optimize_rar(es_info,col_ratio,col_y,col_x,name_hiret,name_lorisk, \
1685
- colorbartxt,title_ext,ylabeltxt,x_axis_name,graph=graph)
1686
-
1687
- return name_hiret,hiret_weights,name_lorisk,lorisk_weights,portfolio_returns
1688
-
1689
-
1690
- if __name__=='__main__':
1691
- Market={'Market':('US','^GSPC','我的组合001')}
1692
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
1693
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
1694
- portfolio=dict(Market,**Stocks1,**Stocks2)
1695
-
1696
- pf_info=portfolio_expret(portfolio,'2019-12-31')
1697
- es_alpha=portfolio_es_alpha(pf_info,simulation=50000)
1698
-
1699
- MAR_weights,GMB_weights,portfolio_returns=portfolio_optimize_alpha(es_alpha)
1700
-
1701
- #==============================================================================
1702
-
1703
- def portfolio_optimize_treynor(es_info,RF=True,graph=True):
1704
- """
1705
- 功能:计算投资组合的最高特雷诺比率组合,并绘图
1706
- MTR: Maximium Treynor Ratio, 最高特雷诺指数方案
1707
- GMBT: Global Minimum Beta by Treynor, 全局最小贝塔系数方案
1708
- """
1709
-
1710
- #需要定制:定义名称变量......................................................
1711
- col_ratio='Treynor' #指数名称
1712
- col_y='Risk premium' #指数分子
1713
- col_x='beta' #指数分母
1714
-
1715
- name_hiret='MTR' #Maximum Treynor Ratio,指数最高点
1716
- name_lorisk='GMBT' #Global Minimum Beta in Treynor,风险最低点
1717
-
1718
- title_ext="特雷诺比率" #用于标题区别
1719
- if RF:
1720
- colorbartxt='特雷诺比率(经无风险利率调整后)' #用于彩色棒标签
1721
- ylabeltxt="年化风险溢价" #用于纵轴名称
1722
- else:
1723
- colorbartxt='特雷诺比率(未经无风险利率调整)' #用于彩色棒标签
1724
- ylabeltxt="年化收益率" #用于纵轴名称
1725
- x_axis_name="贝塔系数" #用于横轴名称
1726
- #定制部分结束...............................................................
1727
-
1728
- #计算指数,寻找最大指数点和风险最低点,并绘图标注两个点
1729
- hiret_weights,lorisk_weights,portfolio_returns = \
1730
- portfolio_optimize_rar(es_info,col_ratio,col_y,col_x,name_hiret,name_lorisk, \
1731
- colorbartxt,title_ext,ylabeltxt,x_axis_name,graph=graph)
1732
-
1733
- return name_hiret,hiret_weights,name_lorisk,lorisk_weights,portfolio_returns
1734
-
1735
- #==============================================================================
1736
-
1737
-
1738
- def portfolio_optimize_rar(es_info,col_ratio,col_y,col_x,name_hiret,name_lorisk, \
1739
- colorbartxt,title_ext,ylabeltxt,x_axis_name,graph=True):
1740
- """
1741
- 功能:提供rar比率优化的共同处理部分
1742
- 基于RandomPortfolios中的随机投资组合,计算相应的指数,寻找最大指数点和风险最小点,并绘图标注两个点
1743
- 输入:以特雷诺比率为例
1744
- col_ratio='Treynor' #指数名称
1745
- col_y='Risk premium' #指数分子
1746
- col_x='beta' #指数分母
1747
- name_hiret='MTR' #Maximum Treynor Ratio,指数最高点
1748
- name_lorisk='GMBT' #Global Minimum Beta in Treynor,风险最低点
1749
-
1750
- colorbartxt='特雷诺比率' #用于彩色棒标签
1751
- title_ext="特雷诺比率" #用于标题区别
1752
- ylabeltxt="年化风险溢价" #用于纵轴名称
1753
- x_axis_name="贝塔系数" #用于横轴名称
1754
-
1755
- """
1756
- #解析传入的数据
1757
- [[[portfolio,thedate,stock_return,_,_],[StockReturns,_,_,_]],RandomPortfolios]=es_info
1758
- _,_,tickerlist,_,ticker_type=decompose_portfolio(portfolio)
1759
- numstocks=len(tickerlist)
1760
- pname=portfolio_name(portfolio)
1761
-
1762
- #取出观察期
1763
- hstart0=StockReturns.index[0]; hstart=str(hstart0.date())
1764
- hend0=StockReturns.index[-1]; hend=str(hend0.date())
1765
-
1766
- #识别并计算指数..........................................................
1767
- if col_ratio in ['Alpha']:
1768
- RandomPortfolios[col_ratio] = RandomPortfolios[col_y]
1769
- elif col_ratio in ['Treynor','Sharpe','Sortino']:
1770
- RandomPortfolios[col_ratio] = RandomPortfolios[col_y] / RandomPortfolios[col_x]
1771
- else:
1772
- print(" #Error(portfolio_optimize_rar): invalid rar",col_ratio)
1773
- print(" Supported rar(risk-adjusted-return): Treynor, Sharpe, Sortino, Alpha")
1774
- return None
1775
-
1776
- # 找到指数最大数据对应的索引值
1777
- max_index = RandomPortfolios[col_ratio].idxmax()
1778
- # 找出指数最大的点坐标并绘制该点
1779
- hiret_x = RandomPortfolios.loc[max_index,col_x]
1780
- hiret_y = RandomPortfolios.loc[max_index,col_y]
1781
-
1782
- # 提取最高指数组合对应的权重,并转化为numpy数组
1783
- import numpy as np
1784
- hiret_weights = np.array(RandomPortfolios.iloc[max_index, 0:numstocks])
1785
- # 计算最高指数组合的收益率
1786
- StockReturns['Portfolio_'+name_hiret] = stock_return[tickerlist].mul(hiret_weights, axis=1).sum(axis=1)
1787
-
1788
- # 找到风险最小组合的索引值
1789
- min_index = RandomPortfolios[col_x].idxmin()
1790
- # 提取最小风险组合对应的权重, 并转换成Numpy数组
1791
- # 找出风险最小的点坐标并绘制该点
1792
- lorisk_x = RandomPortfolios.loc[min_index,col_x]
1793
- lorisk_y = RandomPortfolios.loc[min_index,col_y]
1794
-
1795
- # 提取最小风险组合对应的权重,并转化为numpy数组
1796
- lorisk_weights = np.array(RandomPortfolios.iloc[min_index, 0:numstocks])
1797
- # 计算风险最小组合的收益率
1798
- StockReturns['Portfolio_'+name_lorisk] = stock_return[tickerlist].mul(lorisk_weights, axis=1).sum(axis=1)
1799
-
1800
- #绘制散点图
1801
- simulation=len(RandomPortfolios)
1802
-
1803
- lang = check_language()
1804
- if lang == 'Chinese':
1805
- point_txt="点"
1806
- else:
1807
- point_txt=" Point"
1808
-
1809
- hiret_point=[hiret_x,hiret_y,name_hiret+point_txt]
1810
- lorisk_point=[lorisk_x,lorisk_y,name_lorisk+point_txt]
1811
- if graph:
1812
- RandomPortfolios_plot(RandomPortfolios,col_x,col_y,colorbartxt,title_ext, \
1813
- ylabeltxt,x_axis_name,pname,simulation,hstart,hend, \
1814
- hiret_point,lorisk_point)
1815
-
1816
- #返回数据,供进一步分析
1817
- portfolio_returns=StockReturns.copy()
1818
-
1819
- #将投资组合策略改为中文
1820
- portfolio_returns=cvt_portfolio_name(pname,portfolio_returns)
1821
-
1822
- return hiret_weights,lorisk_weights,portfolio_returns
1823
-
1824
-
1825
- if __name__=='__main__':
1826
- Market={'Market':('US','^GSPC','我的组合001')}
1827
- Stocks1={'AAPL':.1,'MSFT':.13,'XOM':.09,'JNJ':.09,'JPM':.09}
1828
- Stocks2={'AMZN':.15,'GE':.08,'FB':.13,'T':.14}
1829
- portfolio=dict(Market,**Stocks1,**Stocks2)
1830
-
1831
- pf_info=portfolio_expret(portfolio,'2019-12-31')
1832
- es_treynor=portfolio_es_treynor(pf_info,simulation=50000)
1833
-
1834
- MTR_weights,GMB2_weights,portfolio_returns=portfolio_optimize_treynor(es_treynor)
1835
-
1836
- #==============================================================================
1837
- #==============================================================================
1838
- if __name__=='__main__':
1839
- ratio='sharpe'
1840
- ratio='alpha'
1841
- ratio='treynor'
1842
- simulation=1000
1843
- simulation=50000
1844
-
1845
- def portfolio_optimize_strategy(pf_info,ratio='sharpe',simulation=50000,RF=False, \
1846
- graph=True,MSR_return=False,GMVS=True):
1847
- """
1848
- 功能:集成式投资组合优化策略
1849
- 注意:实验发现RF对于结果的影响极其微小难以观察,默认设为不使用无风险利率调整收益
1850
- """
1851
-
1852
- ratio_list=['treynor','sharpe','sortino','alpha']
1853
- if not (ratio in ratio_list):
1854
- print(" #Error(portfolio_optimize_strategy): invalid strategy ratio",ratio)
1855
- print(" Supported strategy ratios",ratio_list)
1856
- return
1857
-
1858
- print(" Optimizing portfolio configuration based on",ratio,"ratio ...")
1859
-
1860
- [[portfolio,_,_,_,_],_]=pf_info
1861
- pname=portfolio_name(portfolio)
1862
-
1863
- #观察马科维茨可行集:风险溢价-标准差,用于夏普比率优化
1864
- func_es="portfolio_es_"+ratio
1865
- es_info=eval(func_es)(pf_info=pf_info,simulation=simulation,RF=RF)
1866
-
1867
-
1868
- #寻找比率最优点:最大夏普比率策略MSR和最小风险策略GMV
1869
- func_optimize="portfolio_optimize_"+ratio
1870
- name_hiret,hiret_weights,name_lorisk,lorisk_weights,portfolio_returns= \
1871
- eval(func_optimize)(es_info=es_info,RF=RF,graph=graph)
1872
-
1873
- lang = check_language()
1874
- if lang == 'Chinese':
1875
- zhuhe_txt='组合'
1876
- mingcheng_txt='名称'
1877
- titletxt="投资组合策略:业绩比较"
1878
- ylabeltxt="持有收益率"
1879
- else:
1880
- zhuhe_txt=''
1881
- mingcheng_txt='Portfolio'
1882
- titletxt="Investment Portfolio Strategy: Performance Comparison"
1883
- ylabeltxt="Holding Return"
1884
-
1885
- #打印投资组合构造和业绩表现
1886
- hi_name=name_hiret+zhuhe_txt
1887
- lo_name=name_lorisk+zhuhe_txt
1888
- portfolio_expectation(hi_name,pf_info,hiret_weights)
1889
-
1890
- if MSR_return:
1891
- scope,mktidx,tickerlist,_,ticker_type=decompose_portfolio(portfolio)
1892
- hwdf=pd.DataFrame(hiret_weights)
1893
- hwdft=hwdf.T
1894
- hwdft.columns=tickerlist
1895
- hwdftt=hwdft.T
1896
- hwdftt.sort_values(by=[0],ascending=False,inplace=True)
1897
- hwdftt['ticker']=hwdftt.index
1898
- hwdftt['weight']=hwdftt[0].apply(lambda x:round(x,4))
1899
- stocks_new=hwdftt.set_index(['ticker'])['weight'].to_dict()
1900
- pname=portfolio_name(portfolio)
1901
-
1902
- Market={'Market':(scope,mktidx,pname)}
1903
- portfolio_new=dict(Market,**stocks_new)
1904
-
1905
- if GMVS:
1906
- portfolio_expectation(lo_name,pf_info,lorisk_weights)
1907
-
1908
- #现有投资组合的排名
1909
- ranks=portfolio_ranks(portfolio_returns,pname)
1910
-
1911
- #绘制投资组合策略业绩比较曲线:最多显示4条曲线,否则黑白打印时无法区分
1912
- top4=list(ranks[mingcheng_txt])[:4]
1913
- for p in top4:
1914
- if p in [pname,hi_name,lo_name]:
1915
- continue
1916
- else:
1917
- break
1918
- name_list=[pname,hi_name,lo_name,p]
1919
-
1920
- if graph:
1921
- portfolio_expret_plot(portfolio_returns,name_list,titletxt=titletxt,ylabeltxt=ylabeltxt)
1922
-
1923
- if MSR_return:
1924
- return portfolio_new
1925
- else:
1926
- return
1927
-
1928
- #==============================================================================
1929
- #==============================================================================
1930
- #==============================================================================
1931
- #==============================================================================
1932
- #==============================================================================
1933
- #==============================================================================
1934
-
1935
- def translate_tickerlist(tickerlist):
1936
- newlist=[]
1937
- for t in tickerlist:
1938
- name=ticker_name(t,'bond')
1939
- newlist=newlist+[name]
1940
-
1941
- return newlist
1942
- #==============================================================================
1943
- # 绘制马科维茨有效边界
1944
- #==============================================================================
1945
- def ret_monthly(ticker,prices):
1946
- """
1947
- 功能:
1948
- """
1949
- price=prices['Adj Close'][ticker]
1950
-
1951
- import numpy as np
1952
- div=price.pct_change()+1
1953
- logret=np.log(div)
1954
- import pandas as pd
1955
- lrdf=pd.DataFrame(logret)
1956
- lrdf['ymd']=lrdf.index.astype("str")
1957
- lrdf['ym']=lrdf['ymd'].apply(lambda x:x[0:7])
1958
- lrdf.dropna(inplace=True)
1959
-
1960
- mret=lrdf.groupby(by=['ym'])[ticker].sum()
1961
-
1962
- return mret
1963
-
1964
- if __name__=='__main__':
1965
- ticker='MSFT'
1966
- fromdate,todate='2019-1-1','2020-8-1'
1967
-
1968
- #==============================================================================
1969
- def objFunction(W,R,target_ret):
1970
-
1971
- import numpy as np
1972
- stock_mean=np.mean(R,axis=0)
1973
- port_mean=np.dot(W,stock_mean) # portfolio mean
1974
-
1975
- cov=np.cov(R.T) # var-cov matrix
1976
- port_var=np.dot(np.dot(W,cov),W.T) # portfolio variance
1977
- penalty = 2000*abs(port_mean-target_ret)# penalty 4 deviation
1978
-
1979
- objfunc=np.sqrt(port_var) + penalty # objective function
1980
-
1981
- return objfunc
1982
-
1983
- #==============================================================================
1984
- def portfolio_ef_0(stocks,fromdate,todate):
1985
- """
1986
- 功能:绘制马科维茨有效前沿,不区分上半沿和下半沿
1987
- 问题:很可能出现上下边界折叠的情况,难以解释,弃用
1988
- """
1989
- #Code for getting stock prices
1990
- prices=get_prices(stocks,fromdate,todate)
1991
-
1992
- #Code for generating a return matrix R
1993
- R0=ret_monthly(stocks[0],prices) # starting from 1st stock
1994
- n_stock=len(stocks) # number of stocks
1995
- import pandas as pd
1996
- import numpy as np
1997
- for i in range(1,n_stock): # merge with other stocks
1998
- x=ret_monthly(stocks[i],prices)
1999
- R0=pd.merge(R0,x,left_index=True,right_index=True)
2000
- R=np.array(R0)
2001
-
2002
- #Code for estimating optimal portfolios for a given return
2003
- out_mean,out_std,out_weight=[],[],[]
2004
- import numpy as np
2005
- stockMean=np.mean(R,axis=0)
2006
-
2007
- from scipy.optimize import minimize
2008
- for r in np.linspace(np.min(stockMean),np.max(stockMean),num=100):
2009
- W = np.ones([n_stock])/n_stock # starting from equal weights
2010
- b_ = [(0,1) for i in range(n_stock)] # bounds, here no short
2011
- c_ = ({'type':'eq', 'fun': lambda W: sum(W)-1. }) #constraint
2012
- result=minimize(objFunction,W,(R,r),method='SLSQP'
2013
- ,constraints=c_, bounds=b_)
2014
- if not result.success: # handle error raise
2015
- BaseException(result.message)
2016
-
2017
- try:
2018
- out_mean.append(round(r,4)) # 4 decimal places
2019
- except:
2020
- out_mean._append(round(r,4))
2021
-
2022
- std_=round(np.std(np.sum(R*result.x,axis=1)),6)
2023
- try:
2024
- out_std.append(std_)
2025
- out_weight.append(result.x)
2026
- except:
2027
- out_std._append(std_)
2028
- out_weight._append(result.x)
2029
-
2030
- #Code for plotting the efficient frontier
2031
-
2032
- plt.title('Efficient Frontier of Portfolio')
2033
- plt.xlabel('Standard Deviation of portfolio (Risk))')
2034
- plt.ylabel('Return of portfolio')
2035
-
2036
- out_std_min=min(out_std)
2037
- pos=out_std.index(out_std_min)
2038
- out_mean_min=out_mean[pos]
2039
- x_left=out_std_min+0.25
2040
- y_left=out_mean_min+0.5
2041
-
2042
- #plt.figtext(x_left,y_left,str(n_stock)+' stock are used: ')
2043
- plt.figtext(x_left,y_left,"投资组合由"+str(n_stock)+'种证券构成: ')
2044
- plt.figtext(x_left,y_left-0.05,' '+str(stocks))
2045
- plt.figtext(x_left,y_left-0.1,'观察期间:'+str(fromdate)+'至'+str(todate))
2046
- plt.plot(out_std,out_mean,color='r',ls=':',lw=4)
2047
-
2048
- plt.gca().set_facecolor('whitesmoke')
2049
- plt.show()
2050
-
2051
- return
2052
-
2053
- if __name__=='__main__':
2054
- stocks=['IBM','WMT','AAPL','C','MSFT']
2055
- fromdate,todate='2019-1-1','2020-8-1'
2056
- portfolio_ef_0(stocks,fromdate,todate)
2057
-
2058
- #==============================================================================
2059
- def portfolio_ef(stocks,fromdate,todate):
2060
- """
2061
- 功能:多只股票的马科维茨有效边界,区分上半沿和下半沿,标记风险极小点
2062
- 问题:很可能出现上下边界折叠的情况,难以解释,弃用
2063
- """
2064
- print("\n Searching for portfolio information, please wait...")
2065
- #Code for getting stock prices
2066
- prices=get_prices(stocks,fromdate,todate)
2067
-
2068
- #Code for generating a return matrix R
2069
- R0=ret_monthly(stocks[0],prices) # starting from 1st stock
2070
- n_stock=len(stocks) # number of stocks
2071
-
2072
- import pandas as pd
2073
- import numpy as np
2074
- for i in range(1,n_stock): # merge with other stocks
2075
- x=ret_monthly(stocks[i],prices)
2076
- R0=pd.merge(R0,x,left_index=True,right_index=True)
2077
- R=np.array(R0)
2078
-
2079
- #Code for estimating optimal portfolios for a given return
2080
- out_mean,out_std,out_weight=[],[],[]
2081
- stockMean=np.mean(R,axis=0)
2082
-
2083
- from scipy.optimize import minimize
2084
- for r in np.linspace(np.min(stockMean),np.max(stockMean),num=100):
2085
- W = np.ones([n_stock])/n_stock # starting from equal weights
2086
- b_ = [(0,1) for i in range(n_stock)] # bounds, here no short
2087
- c_ = ({'type':'eq', 'fun': lambda W: sum(W)-1. }) #constraint
2088
- result=minimize(objFunction,W,(R,r),method='SLSQP'
2089
- ,constraints=c_, bounds=b_)
2090
- if not result.success: # handle error raise
2091
- BaseException(result.message)
2092
-
2093
- try:
2094
- out_mean.append(round(r,4)) # 4 decimal places
2095
- std_=round(np.std(np.sum(R*result.x,axis=1)),6)
2096
- out_std.append(std_)
2097
- out_weight.append(result.x)
2098
- except:
2099
- out_mean._append(round(r,4)) # 4 decimal places
2100
- std_=round(np.std(np.sum(R*result.x,axis=1)),6)
2101
- out_std._append(std_)
2102
- out_weight._append(result.x)
2103
-
2104
- #Code for positioning
2105
- out_std_min=min(out_std)
2106
- pos=out_std.index(out_std_min)
2107
- out_mean_min=out_mean[pos]
2108
- x_left=out_std_min+0.25
2109
- y_left=out_mean_min+0.5
2110
-
2111
- import pandas as pd
2112
- out_df=pd.DataFrame(out_mean,out_std,columns=['mean'])
2113
- out_df_ef=out_df[out_df['mean']>=out_mean_min]
2114
- out_df_ief=out_df[out_df['mean']<out_mean_min]
2115
-
2116
- #Code for plotting the efficient frontier
2117
-
2118
- plt.title('投资组合:马科维茨有效边界(理想图)')
2119
-
2120
- import datetime as dt; stoday=dt.date.today()
2121
- plt.xlabel('收益率标准差-->'+"\n数据来源:新浪/EM/stooq, "+str(stoday))
2122
- plt.ylabel('收益率')
2123
-
2124
- plt.figtext(x_left,y_left,"投资组合由"+str(n_stock)+'种证券构成: ')
2125
- plt.figtext(x_left,y_left-0.05,' '+str(stocks))
2126
- plt.figtext(x_left,y_left-0.1,'观察期间:'+str(fromdate)+'至'+str(todate))
2127
- plt.plot(out_df_ef.index,out_df_ef['mean'],color='r',ls='--',lw=2,label='有效边界')
2128
- plt.plot(out_df_ief.index,out_df_ief['mean'],color='k',ls=':',lw=2,label='无效边界')
2129
- plt.plot(out_std_min,out_mean_min,'g*-',markersize=16,label='风险最低点')
2130
-
2131
- plt.legend(loc='best')
2132
- plt.gca().set_facecolor('whitesmoke')
2133
- plt.show()
2134
-
2135
- return
2136
-
2137
- if __name__=='__main__':
2138
- stocks=['IBM','WMT','AAPL','C','MSFT']
2139
- fromdate,todate='2019-1-1','2020-8-1'
2140
- df=portfolio_ef(stocks,fromdate,todate)
2141
-
2142
- #==============================================================================
2143
- if __name__=='__main__':
2144
- tickers=['^GSPC','000001.SS','^HSI','^N225','^BSESN']
2145
- start='2023-1-1'
2146
- end='2023-3-22'
2147
- info_type='Volume'
2148
- df=security_correlation(tickers,start,end,info_type='Close')
2149
-
2150
-
2151
- def cm2inch(x,y):
2152
- return x/2.54,y/2.54
2153
-
2154
- def security_correlation(tickers,start='L5Y',end='today',info_type='Close'):
2155
- """
2156
- ===========================================================================
2157
- 功能:股票/指数收盘价之间的相关性
2158
- 参数:
2159
- tickers:指标列表,至少两个
2160
- start:起始日期,格式YYYY-MM-DD,支持简易格式
2161
- end:截止日期
2162
- info_type:指标的数值类型,默认'Close', 还可为Open/High/Low/Volume
2163
- """
2164
-
2165
- start,end=start_end_preprocess(start,end)
2166
-
2167
- info_types=['Close','Open','High','Low','Volume']
2168
- info_types_cn=['收盘价','开盘价','最高价','最低价','成交量']
2169
- if not(info_type in info_types):
2170
- print(" #Error(security_correlation): invalid information type",info_type)
2171
- print(" Supported information type:",info_types)
2172
- return None
2173
- pos=info_types.index(info_type)
2174
- info_type_cn=info_types_cn[pos]
2175
-
2176
- #屏蔽函数内print信息输出的类
2177
- import os, sys
2178
- class HiddenPrints:
2179
- def __enter__(self):
2180
- self._original_stdout = sys.stdout
2181
- sys.stdout = open(os.devnull, 'w')
2182
-
2183
- def __exit__(self, exc_type, exc_val, exc_tb):
2184
- sys.stdout.close()
2185
- sys.stdout = self._original_stdout
2186
-
2187
- print(" Searching for security prices, please wait ...")
2188
- with HiddenPrints():
2189
- prices=get_prices_simple(tickers,start,end)
2190
- df=prices[info_type]
2191
- df.dropna(axis=0,inplace=True)
2192
-
2193
- # here put the import lib
2194
- import seaborn as sns
2195
- sns.set(font='SimHei') # 解决Seaborn中文显示问题
2196
- #sns.set_style('whitegrid',{'font.sans-serif':['SimHei','Arial']})
2197
- #sns.set_style('whitegrid',{'font.sans-serif':['FangSong']})
2198
-
2199
- import numpy as np
2200
- from scipy.stats import pearsonr
2201
-
2202
- collist=list(df)
2203
- for col in collist:
2204
- df.rename(columns={col:ticker_name(col,'bond')},inplace=True)
2205
- df_coor = df.corr()
2206
-
2207
-
2208
- #fig = plt.figure(figsize=(12.8,7.2))
2209
- fig = plt.figure(figsize=(12.8,6.4))
2210
- ax1 = plt.gca()
2211
-
2212
- #构造mask,去除重复数据显示
2213
- mask = np.zeros_like(df_coor)
2214
- mask[np.triu_indices_from(mask)] = True
2215
- mask2 = mask
2216
- mask = (np.flipud(mask)-1)*(-1)
2217
- mask = np.rot90(mask,k = -1)
2218
-
2219
- im1 = sns.heatmap(df_coor,annot=True,cmap="YlGnBu"
2220
- , mask=mask#构造mask,去除重复数据显示
2221
- ,vmax=1,vmin=-1
2222
- , fmt='.2f',ax = ax1,annot_kws={"size": 5})
2223
-
2224
- ax1.tick_params(axis = 'both', length=0)
2225
-
2226
- #计算相关性显著性并显示
2227
- rlist = []
2228
- plist = []
2229
- for i in df.columns.values:
2230
- for j in df.columns.values:
2231
- r,p = pearsonr(df[i],df[j])
2232
- try:
2233
- rlist.append(r)
2234
- plist.append(p)
2235
- except:
2236
- rlist._append(r)
2237
- plist._append(p)
2238
-
2239
- rarr = np.asarray(rlist).reshape(len(df.columns.values),len(df.columns.values))
2240
- parr = np.asarray(plist).reshape(len(df.columns.values),len(df.columns.values))
2241
- xlist = ax1.get_xticks()
2242
- ylist = ax1.get_yticks()
2243
-
2244
- widthx = 0
2245
- widthy = -0.15
2246
-
2247
- # 星号的大小
2248
- font_dict={'size':5}
2249
-
2250
- for m in ax1.get_xticks():
2251
- for n in ax1.get_yticks():
2252
- pv = (parr[int(m),int(n)])
2253
- rv = (rarr[int(m),int(n)])
2254
- if mask2[int(m),int(n)]<1.:
2255
- if abs(rv) > 0.5:
2256
- if pv< 0.05 and pv>= 0.01:
2257
- ax1.text(n+widthx,m+widthy,'*',ha = 'center',color = 'white',fontdict=font_dict)
2258
- if pv< 0.01 and pv>= 0.001:
2259
- ax1.text(n+widthx,m+widthy,'**',ha = 'center',color = 'white',fontdict=font_dict)
2260
- if pv< 0.001:
2261
- #print([int(m),int(n)])
2262
- ax1.text(n+widthx,m+widthy,'***',ha = 'center',color = 'white',fontdict=font_dict)
2263
- else:
2264
- if pv< 0.05 and pv>= 0.01:
2265
- ax1.text(n+widthx,m+widthy,'*',ha = 'center',color = 'k',fontdict=font_dict)
2266
- elif pv< 0.01 and pv>= 0.001:
2267
- ax1.text(n+widthx,m+widthy,'**',ha = 'center',color = 'k',fontdict=font_dict)
2268
- elif pv< 0.001:
2269
- ax1.text(n+widthx,m+widthy,'***',ha = 'center',color = 'k',fontdict=font_dict)
2270
-
2271
- plt.title("序列相关性分析:"+info_type_cn)
2272
- plt.tick_params(labelsize=5)
2273
-
2274
- footnote1="\n显著性数值:***非常显著(<0.001),**很显著(<0.01),*显著(<0.05),其余为不显著"
2275
- footnote2="\n系数绝对值:>=0.8极强相关,0.6-0.8强相关,0.4-0.6相关,0.2-0.4弱相关,否则为极弱(不)相关"
2276
-
2277
- footnote3="\n观察期间: "+start+'至'+end
2278
- import datetime as dt; stoday=dt.date.today()
2279
- footnote4=";数据来源:Sina/EM/Stooq/Yahoo,"+str(stoday)
2280
-
2281
- fontxlabel={'size':5}
2282
- plt.xlabel(footnote1+footnote2+footnote3+footnote4,fontxlabel)
2283
- #plt.xticks(rotation=45)
2284
-
2285
- plt.gca().set_facecolor('whitesmoke')
2286
- plt.show()
2287
-
2288
- return df_coor
2289
-
2290
- #==============================================================================
2291
- if __name__ =="__main__":
2292
- portfolio={'Market':('US','^GSPC','Test 1'),'EDU':0.4,'TAL':0.3,'TEDU':0.2}
2293
-
2294
- def describe_portfolio(portfolio):
2295
- """
2296
- 功能:描述投资组合的信息
2297
- 输入:投资组合
2298
- 输出:市场,市场指数,股票代码列表和份额列表
2299
- """
2300
-
2301
- scope,mktidx,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
2302
- pname=portfolio_name(portfolio)
2303
-
2304
- print("*** 投资组合信息:",pname)
2305
- print("\n所在市场:",ectranslate(scope))
2306
- print("市场指数:",ticker_name(mktidx,'bond')+'('+mktidx+')')
2307
- print("成分股及其份额:")
2308
-
2309
- num=len(tickerlist)
2310
- #seqlist=[]
2311
- tickerlist1=[]
2312
- sharelist1=[]
2313
- for t in range(num):
2314
- #seqlist=seqlist+[t+1]
2315
- tickerlist1=tickerlist1+[ticker_name(tickerlist[t],'bond')+'('+tickerlist[t]+')']
2316
- sharelist1=sharelist1+[str(round(sharelist[t],2))+'%']
2317
-
2318
- import pandas as pd
2319
- #df=pd.DataFrame({'序号':seqlist,'成分股':tickerlist1,'份额':sharelist1})
2320
- df=pd.DataFrame({'成分股':tickerlist1,'份额':sharelist1})
2321
- df.index=df.index+1
2322
-
2323
- alignlist=['center','left','right']
2324
- print(df.to_markdown(index=True,tablefmt='plain',colalign=alignlist))
2325
-
2326
- return
2327
-
2328
- #==============================================================================
2329
- def portfolio_drop(portfolio,last=0,droplist=[],new_name=''):
2330
- """
2331
- 功能:删除最后几个成分股
2332
- """
2333
- scope,mktidx,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
2334
- pname=portfolio_name(portfolio)
2335
-
2336
- if not (last ==0):
2337
- for i in range(last):
2338
- #print(i)
2339
- tmp=tickerlist.pop()
2340
- tmp=sharelist.pop()
2341
-
2342
- if not (droplist==[]):
2343
- for d in droplist:
2344
- pos=tickerlist.index(d)
2345
- tmp=tickerlist.pop(pos)
2346
- tmp=sharelist.pop(pos)
2347
-
2348
- stocks_new=dict(zip(tickerlist,sharelist))
2349
-
2350
- if new_name=='':
2351
- new_name=pname
2352
-
2353
- Market={'Market':(scope,mktidx,new_name)}
2354
- portfolio_new=dict(Market,**stocks_new)
2355
-
2356
- return portfolio_new
2357
-
2358
- #==============================================================================
2359
- #==============================================================================
2360
- #==============================================================================
2361
- #==============================================================================
2362
-
2363
-